From 5e7baf0fcb2a3aef7329f3c7543d4695a46bd321 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 9 Aug 2018 11:13:49 -0700 Subject: [PATCH 1/3] qed/qede: Multi CoS support. This patch adds support for tc mqprio offload, using this different traffic classes on the adapter can be utilized based on configured priority to tc map. For example - tc qdisc add dev eth0 root mqprio num_tc 4 map 0 1 2 3 This will cause SKBs with priority 0,1,2,3 to transmit over tc 0,1,2,3 hardware queues respectively. Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_l2.c | 9 +- drivers/net/ethernet/qlogic/qed/qed_main.c | 5 +- drivers/net/ethernet/qlogic/qede/qede.h | 13 ++ .../net/ethernet/qlogic/qede/qede_ethtool.c | 48 ++++-- drivers/net/ethernet/qlogic/qede/qede_fp.c | 29 ++-- drivers/net/ethernet/qlogic/qede/qede_main.c | 139 ++++++++++++++---- include/linux/qed/qed_eth_if.h | 6 + 7 files changed, 200 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 5ede6408649d..82a1bd1f8a8c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -2188,16 +2188,17 @@ int qed_get_queue_coalesce(struct qed_hwfn *p_hwfn, u16 *p_coal, void *handle) static int qed_fill_eth_dev_info(struct qed_dev *cdev, struct qed_dev_eth_info *info) { + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); int i; memset(info, 0, sizeof(*info)); - info->num_tc = 1; - if (IS_PF(cdev)) { int max_vf_vlan_filters = 0; int max_vf_mac_filters = 0; + info->num_tc = p_hwfn->hw_info.num_hw_tc; + if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) { u16 num_queues = 0; @@ -2248,6 +2249,8 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev, } else { u16 total_cids = 0; + info->num_tc = 1; + /* Determine queues & XDP support */ for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; @@ -2554,7 +2557,7 @@ static int qed_start_txq(struct qed_dev *cdev, rc = qed_eth_tx_queue_start(p_hwfn, p_hwfn->hw_info.opaque_fid, - p_params, 0, + p_params, p_params->tc, pbl_addr, pbl_size, ret_params); if (rc) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index dbe81310c0b6..2094d86a7a08 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -948,13 +948,14 @@ static void qed_update_pf_params(struct qed_dev *cdev, params->eth_pf_params.num_arfs_filters = 0; /* In case we might support RDMA, don't allow qede to be greedy - * with the L2 contexts. Allow for 64 queues [rx, tx, xdp] per hwfn. + * with the L2 contexts. Allow for 64 queues [rx, tx cos, xdp] + * per hwfn. */ if (QED_IS_RDMA_PERSONALITY(QED_LEADING_HWFN(cdev))) { u16 *num_cons; num_cons = ¶ms->eth_pf_params.num_cons; - *num_cons = min_t(u16, *num_cons, 192); + *num_cons = min_t(u16, *num_cons, QED_MAX_L2_CONS); } for (i = 0; i < cdev->num_hwfns; i++) { diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index d7ed0d3dbf71..e90c60a8fb01 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -386,6 +386,15 @@ struct qede_tx_queue { #define QEDE_TXQ_XDP_TO_IDX(edev, txq) ((txq)->index - \ QEDE_MAX_TSS_CNT(edev)) #define QEDE_TXQ_IDX_TO_XDP(edev, idx) ((idx) + QEDE_MAX_TSS_CNT(edev)) +#define QEDE_NDEV_TXQ_ID_TO_FP_ID(edev, idx) ((edev)->fp_num_rx + \ + ((idx) % QEDE_TSS_COUNT(edev))) +#define QEDE_NDEV_TXQ_ID_TO_TXQ_COS(edev, idx) ((idx) / QEDE_TSS_COUNT(edev)) +#define QEDE_TXQ_TO_NDEV_TXQ_ID(edev, txq) ((QEDE_TSS_COUNT(edev) * \ + (txq)->cos) + (txq)->index) +#define QEDE_NDEV_TXQ_ID_TO_TXQ(edev, idx) \ + (&((edev)->fp_array[QEDE_NDEV_TXQ_ID_TO_FP_ID(edev, idx)].txq \ + [QEDE_NDEV_TXQ_ID_TO_TXQ_COS(edev, idx)])) +#define QEDE_FP_TC0_TXQ(fp) (&((fp)->txq[0])) /* Regular Tx requires skb + metadata for release purpose, * while XDP requires the pages and the mapped address. @@ -399,6 +408,8 @@ struct qede_tx_queue { /* Slowpath; Should be kept in end [unless missing padding] */ void *handle; + u16 cos; + u16 ndev_txq_id; }; #define BD_UNMAP_ADDR(bd) HILO_U64(le32_to_cpu((bd)->addr.hi), \ @@ -541,5 +552,7 @@ void qede_update_rx_prod(struct qede_dev *edev, struct qede_rx_queue *rxq); #define QEDE_RX_HDR_SIZE 256 #define QEDE_MAX_JUMBO_PACKET_SIZE 9600 #define for_each_queue(i) for (i = 0; i < edev->num_queues; i++) +#define for_each_cos_in_txq(edev, var) \ + for ((var) = 0; (var) < (edev)->dev_info.num_tc; (var)++) #endif /* _QEDE_H_ */ diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index b37857f3f950..2bd84d6d9b15 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -222,7 +222,7 @@ static void qede_get_strings_stats_txq(struct qede_dev *edev, QEDE_TXQ_XDP_TO_IDX(edev, txq), qede_tqstats_arr[i].string); else - sprintf(*buf, "%d: %s", txq->index, + sprintf(*buf, "%d_%d: %s", txq->index, txq->cos, qede_tqstats_arr[i].string); *buf += ETH_GSTRING_LEN; } @@ -262,8 +262,13 @@ static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) if (fp->type & QEDE_FASTPATH_XDP) qede_get_strings_stats_txq(edev, fp->xdp_tx, &buf); - if (fp->type & QEDE_FASTPATH_TX) - qede_get_strings_stats_txq(edev, fp->txq, &buf); + if (fp->type & QEDE_FASTPATH_TX) { + int cos; + + for_each_cos_in_txq(edev, cos) + qede_get_strings_stats_txq(edev, + &fp->txq[cos], &buf); + } } /* Account for non-queue statistics */ @@ -338,8 +343,12 @@ static void qede_get_ethtool_stats(struct net_device *dev, if (fp->type & QEDE_FASTPATH_XDP) qede_get_ethtool_stats_txq(fp->xdp_tx, &buf); - if (fp->type & QEDE_FASTPATH_TX) - qede_get_ethtool_stats_txq(fp->txq, &buf); + if (fp->type & QEDE_FASTPATH_TX) { + int cos; + + for_each_cos_in_txq(edev, cos) + qede_get_ethtool_stats_txq(&fp->txq[cos], &buf); + } } for (i = 0; i < QEDE_NUM_STATS; i++) { @@ -366,7 +375,8 @@ static int qede_get_sset_count(struct net_device *dev, int stringset) num_stats--; /* Account for the Regular Tx statistics */ - num_stats += QEDE_TSS_COUNT(edev) * QEDE_NUM_TQSTATS; + num_stats += QEDE_TSS_COUNT(edev) * QEDE_NUM_TQSTATS * + edev->dev_info.num_tc; /* Account for the Regular Rx statistics */ num_stats += QEDE_RSS_COUNT(edev) * QEDE_NUM_RQSTATS; @@ -741,9 +751,17 @@ static int qede_get_coalesce(struct net_device *dev, } for_each_queue(i) { + struct qede_tx_queue *txq; + fp = &edev->fp_array[i]; + + /* All TX queues of given fastpath uses same + * coalescing value, so no need to iterate over + * all TCs, TC0 txq should suffice. + */ if (fp->type & QEDE_FASTPATH_TX) { - tx_handle = fp->txq->handle; + txq = QEDE_FP_TC0_TXQ(fp); + tx_handle = txq->handle; break; } } @@ -801,9 +819,17 @@ static int qede_set_coalesce(struct net_device *dev, } if (edev->fp_array[i].type & QEDE_FASTPATH_TX) { + struct qede_tx_queue *txq; + + /* All TX queues of given fastpath uses same + * coalescing value, so no need to iterate over + * all TCs, TC0 txq should suffice. + */ + txq = QEDE_FP_TC0_TXQ(fp); + rc = edev->ops->common->set_coalesce(edev->cdev, 0, txc, - fp->txq->handle); + txq->handle); if (rc) { DP_INFO(edev, "Set TX coalesce error, rc = %d\n", rc); @@ -1385,8 +1411,10 @@ static int qede_selftest_transmit_traffic(struct qede_dev *edev, u16 val; for_each_queue(i) { - if (edev->fp_array[i].type & QEDE_FASTPATH_TX) { - txq = edev->fp_array[i].txq; + struct qede_fastpath *fp = &edev->fp_array[i]; + + if (fp->type & QEDE_FASTPATH_TX) { + txq = QEDE_FP_TC0_TXQ(fp); break; } } diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index 8c9e95ba9917..1a78027de071 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -408,12 +408,12 @@ static void qede_xdp_tx_int(struct qede_dev *edev, struct qede_tx_queue *txq) static int qede_tx_int(struct qede_dev *edev, struct qede_tx_queue *txq) { + unsigned int pkts_compl = 0, bytes_compl = 0; struct netdev_queue *netdev_txq; u16 hw_bd_cons; - unsigned int pkts_compl = 0, bytes_compl = 0; int rc; - netdev_txq = netdev_get_tx_queue(edev->ndev, txq->index); + netdev_txq = netdev_get_tx_queue(edev->ndev, txq->ndev_txq_id); hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr); barrier(); @@ -1365,9 +1365,14 @@ static bool qede_poll_is_more_work(struct qede_fastpath *fp) if (qede_txq_has_work(fp->xdp_tx)) return true; - if (likely(fp->type & QEDE_FASTPATH_TX)) - if (qede_txq_has_work(fp->txq)) - return true; + if (likely(fp->type & QEDE_FASTPATH_TX)) { + int cos; + + for_each_cos_in_txq(fp->edev, cos) { + if (qede_txq_has_work(&fp->txq[cos])) + return true; + } + } return false; } @@ -1382,8 +1387,14 @@ int qede_poll(struct napi_struct *napi, int budget) struct qede_dev *edev = fp->edev; int rx_work_done = 0; - if (likely(fp->type & QEDE_FASTPATH_TX) && qede_txq_has_work(fp->txq)) - qede_tx_int(edev, fp->txq); + if (likely(fp->type & QEDE_FASTPATH_TX)) { + int cos; + + for_each_cos_in_txq(fp->edev, cos) { + if (qede_txq_has_work(&fp->txq[cos])) + qede_tx_int(edev, &fp->txq[cos]); + } + } if ((fp->type & QEDE_FASTPATH_XDP) && qede_txq_has_work(fp->xdp_tx)) qede_xdp_tx_int(edev, fp->xdp_tx); @@ -1444,8 +1455,8 @@ netdev_tx_t qede_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* Get tx-queue context and netdev index */ txq_index = skb_get_queue_mapping(skb); - WARN_ON(txq_index >= QEDE_TSS_COUNT(edev)); - txq = edev->fp_array[edev->fp_num_rx + txq_index].txq; + WARN_ON(txq_index >= QEDE_TSS_COUNT(edev) * edev->dev_info.num_tc); + txq = QEDE_NDEV_TXQ_ID_TO_TXQ(edev, txq_index); netdev_txq = netdev_get_tx_queue(ndev, txq_index); WARN_ON(qed_chain_get_elem_left(&txq->tx_pbl) < (MAX_SKB_FRAGS + 1)); diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 6a796040a32c..d7299afa902c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -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) { diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 2978fa4add42..a1310482c4ed 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -39,6 +39,10 @@ #include #include +/* 64 max queues * (1 rx + 4 tx-cos + 1 xdp) */ +#define QED_MIN_L2_CONS (2 + NUM_PHYS_TCS_4PORT_K2) +#define QED_MAX_L2_CONS (64 * (QED_MIN_L2_CONS)) + struct qed_queue_start_common_params { /* Should always be relative to entity sending this. */ u8 vport_id; @@ -49,6 +53,8 @@ struct qed_queue_start_common_params { struct qed_sb_info *p_sb; u8 sb_idx; + + u8 tc; }; struct qed_rxq_start_ret_params { From 91a56adbf178fa840069d000fb9d902f30e52456 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 9 Aug 2018 11:13:50 -0700 Subject: [PATCH 2/3] qede: Add destination ip based flow profile. This patch adds support for dropping and redirecting the flows based on destination IP in the packet. This also moves the profile mode settings in their own functions which can be used through tc flows in successive patch. For example - ethtool -N p5p1 flow-type tcp4 dst-ip 192.168.40.100 action -1 ethtool -N p5p1 flow-type udp4 dst-ip 192.168.50.100 action 1 ethtool -N p5p1 flow-type tcp4 dst-ip 192.168.60.100 action 0x100000000 Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- .../net/ethernet/qlogic/qede/qede_filter.c | 114 ++++++++++-------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index f9a327c821eb..d0902573f38a 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -1599,6 +1599,69 @@ static int qede_flow_spec_validate_unused(struct qede_dev *edev, return 0; } +static int qede_set_v4_tuple_to_profile(struct qede_dev *edev, + struct qede_arfs_tuple *t) +{ + /* We must have Only 4-tuples/l4 port/src ip/dst ip + * as an input. + */ + if (t->src_port && t->dst_port && t->src_ipv4 && t->dst_ipv4) { + t->mode = QED_FILTER_CONFIG_MODE_5_TUPLE; + } else if (!t->src_port && t->dst_port && + !t->src_ipv4 && !t->dst_ipv4) { + t->mode = QED_FILTER_CONFIG_MODE_L4_PORT; + } else if (!t->src_port && !t->dst_port && + !t->dst_ipv4 && t->src_ipv4) { + t->mode = QED_FILTER_CONFIG_MODE_IP_SRC; + } else if (!t->src_port && !t->dst_port && + t->dst_ipv4 && !t->src_ipv4) { + t->mode = QED_FILTER_CONFIG_MODE_IP_DEST; + } else { + DP_INFO(edev, "Invalid N-tuple\n"); + return -EOPNOTSUPP; + } + + t->ip_comp = qede_flow_spec_ipv4_cmp; + t->build_hdr = qede_flow_build_ipv4_hdr; + t->stringify = qede_flow_stringify_ipv4_hdr; + + return 0; +} + +static int qede_set_v6_tuple_to_profile(struct qede_dev *edev, + struct qede_arfs_tuple *t, + struct in6_addr *zaddr) +{ + /* We must have Only 4-tuples/l4 port/src ip/dst ip + * as an input. + */ + if (t->src_port && t->dst_port && + memcmp(&t->src_ipv6, zaddr, sizeof(struct in6_addr)) && + memcmp(&t->dst_ipv6, zaddr, sizeof(struct in6_addr))) { + t->mode = QED_FILTER_CONFIG_MODE_5_TUPLE; + } else if (!t->src_port && t->dst_port && + !memcmp(&t->src_ipv6, zaddr, sizeof(struct in6_addr)) && + !memcmp(&t->dst_ipv6, zaddr, sizeof(struct in6_addr))) { + t->mode = QED_FILTER_CONFIG_MODE_L4_PORT; + } else if (!t->src_port && !t->dst_port && + !memcmp(&t->dst_ipv6, zaddr, sizeof(struct in6_addr)) && + memcmp(&t->src_ipv6, zaddr, sizeof(struct in6_addr))) { + t->mode = QED_FILTER_CONFIG_MODE_IP_SRC; + } else if (!t->src_port && !t->dst_port && + memcmp(&t->dst_ipv6, zaddr, sizeof(struct in6_addr)) && + !memcmp(&t->src_ipv6, zaddr, sizeof(struct in6_addr))) { + t->mode = QED_FILTER_CONFIG_MODE_IP_DEST; + } else { + DP_INFO(edev, "Invalid N-tuple\n"); + return -EOPNOTSUPP; + } + + t->ip_comp = qede_flow_spec_ipv6_cmp; + t->build_hdr = qede_flow_build_ipv6_hdr; + + return 0; +} + static int qede_flow_spec_to_tuple_ipv4_common(struct qede_dev *edev, struct qede_arfs_tuple *t, struct ethtool_rx_flow_spec *fs) @@ -1638,27 +1701,7 @@ static int qede_flow_spec_to_tuple_ipv4_common(struct qede_dev *edev, t->src_port = fs->h_u.tcp_ip4_spec.psrc; t->dst_port = fs->h_u.tcp_ip4_spec.pdst; - /* We must either have a valid 4-tuple or only dst port - * or only src ip as an input - */ - if (t->src_port && t->dst_port && t->src_ipv4 && t->dst_ipv4) { - t->mode = QED_FILTER_CONFIG_MODE_5_TUPLE; - } else if (!t->src_port && t->dst_port && - !t->src_ipv4 && !t->dst_ipv4) { - t->mode = QED_FILTER_CONFIG_MODE_L4_PORT; - } else if (!t->src_port && !t->dst_port && - !t->dst_ipv4 && t->src_ipv4) { - t->mode = QED_FILTER_CONFIG_MODE_IP_SRC; - } else { - DP_INFO(edev, "Invalid N-tuple\n"); - return -EOPNOTSUPP; - } - - t->ip_comp = qede_flow_spec_ipv4_cmp; - t->build_hdr = qede_flow_build_ipv4_hdr; - t->stringify = qede_flow_stringify_ipv4_hdr; - - return 0; + return qede_set_v4_tuple_to_profile(edev, t); } static int qede_flow_spec_to_tuple_tcpv4(struct qede_dev *edev, @@ -1690,10 +1733,8 @@ static int qede_flow_spec_to_tuple_ipv6_common(struct qede_dev *edev, struct ethtool_rx_flow_spec *fs) { struct in6_addr zero_addr; - void *p; - p = &zero_addr; - memset(p, 0, sizeof(zero_addr)); + memset(&zero_addr, 0, sizeof(zero_addr)); if ((fs->h_u.tcp_ip6_spec.psrc & fs->m_u.tcp_ip6_spec.psrc) != fs->h_u.tcp_ip6_spec.psrc) { @@ -1720,30 +1761,7 @@ static int qede_flow_spec_to_tuple_ipv6_common(struct qede_dev *edev, t->src_port = fs->h_u.tcp_ip6_spec.psrc; t->dst_port = fs->h_u.tcp_ip6_spec.pdst; - /* We must make sure we have a valid 4-tuple or only dest port - * or only src ip as an input - */ - if (t->src_port && t->dst_port && - memcmp(&t->src_ipv6, p, sizeof(struct in6_addr)) && - memcmp(&t->dst_ipv6, p, sizeof(struct in6_addr))) { - t->mode = QED_FILTER_CONFIG_MODE_5_TUPLE; - } else if (!t->src_port && t->dst_port && - !memcmp(&t->src_ipv6, p, sizeof(struct in6_addr)) && - !memcmp(&t->dst_ipv6, p, sizeof(struct in6_addr))) { - t->mode = QED_FILTER_CONFIG_MODE_L4_PORT; - } else if (!t->src_port && !t->dst_port && - !memcmp(&t->dst_ipv6, p, sizeof(struct in6_addr)) && - memcmp(&t->src_ipv6, p, sizeof(struct in6_addr))) { - t->mode = QED_FILTER_CONFIG_MODE_IP_SRC; - } else { - DP_INFO(edev, "Invalid N-tuple\n"); - return -EOPNOTSUPP; - } - - t->ip_comp = qede_flow_spec_ipv6_cmp; - t->build_hdr = qede_flow_build_ipv6_hdr; - - return 0; + return qede_set_v6_tuple_to_profile(edev, t, &zero_addr); } static int qede_flow_spec_to_tuple_tcpv6(struct qede_dev *edev, From 2ce9c93eaca6c67e3fa8828a471738a32cd66770 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 9 Aug 2018 11:13:51 -0700 Subject: [PATCH 3/3] qede: Ingress tc flower offload (drop action) support. The main motive of this patch is to lay down driver's tc offload infrastructure in place. With these changes tc can offload various supported flow profiles (4 tuples, src-ip, dst-ip, l4 port) for the drop action. Dropped flows statistic is a global counter for all the offloaded flows for drop action and is populated in ethtool statistics as common "gft_filter_drop". Examples - tc qdisc add dev p4p1 ingress tc filter add dev p4p1 protocol ipv4 parent ffff: flower \ skip_sw ip_proto tcp dst_ip 192.168.40.200 action drop tc filter add dev p4p1 protocol ipv4 parent ffff: flower \ skip_sw ip_proto udp src_ip 192.168.40.100 action drop tc filter add dev p4p1 protocol ipv4 parent ffff: flower \ skip_sw ip_proto tcp src_ip 192.168.40.100 dst_ip 192.168.40.200 \ src_port 453 dst_port 876 action drop tc filter add dev p4p1 protocol ipv4 parent ffff: flower \ skip_sw ip_proto tcp dst_port 98 action drop Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 7 +- .../net/ethernet/qlogic/qede/qede_ethtool.c | 2 +- .../net/ethernet/qlogic/qede/qede_filter.c | 308 +++++++++++++++++- drivers/net/ethernet/qlogic/qede/qede_main.c | 56 +++- 4 files changed, 362 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index e90c60a8fb01..6a4d266fb8e2 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -52,6 +52,9 @@ #include #include +#include +#include + #define QEDE_MAJOR_VERSION 8 #define QEDE_MINOR_VERSION 33 #define QEDE_REVISION_VERSION 0 @@ -469,7 +472,7 @@ void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc); void qede_free_arfs(struct qede_dev *edev); int qede_alloc_arfs(struct qede_dev *edev); int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info); -int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info); +int qede_delete_flow_filter(struct qede_dev *edev, u64 cookie); int qede_get_cls_rule_entry(struct qede_dev *edev, struct ethtool_rxnfc *cmd); int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, u32 *rule_locs); @@ -535,6 +538,8 @@ bool qede_has_rx_work(struct qede_rx_queue *rxq); int qede_txq_has_work(struct qede_tx_queue *txq); void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, u8 count); void qede_update_rx_prod(struct qede_dev *edev, struct qede_rx_queue *rxq); +int qede_add_tc_flower_fltr(struct qede_dev *edev, __be16 proto, + struct tc_cls_flower_offload *f); #define RX_RING_SIZE_POW 13 #define RX_RING_SIZE ((u16)BIT(RX_RING_SIZE_POW)) diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 2bd84d6d9b15..19652cd27ca7 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -1285,7 +1285,7 @@ static int qede_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) rc = qede_add_cls_rule(edev, info); break; case ETHTOOL_SRXCLSRLDEL: - rc = qede_del_cls_rule(edev, info); + rc = qede_delete_flow_filter(edev, info->fs.location); break; default: DP_INFO(edev, "Command parameters not supported\n"); diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index d0902573f38a..9673d19308e6 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -83,7 +83,7 @@ struct qede_arfs_fltr_node { struct qede_arfs_tuple tuple; u32 flow_id; - u16 sw_id; + u64 sw_id; u16 rxq_id; u16 next_rxq_id; u8 vfid; @@ -138,7 +138,7 @@ static void qede_configure_arfs_fltr(struct qede_dev *edev, n->tuple.stringify(&n->tuple, tuple_buffer); DP_VERBOSE(edev, NETIF_MSG_RX_STATUS, - "%s sw_id[0x%x]: %s [vf %u queue %d]\n", + "%s sw_id[0x%llx]: %s [vf %u queue %d]\n", add_fltr ? "Adding" : "Deleting", n->sw_id, tuple_buffer, n->vfid, rxq_id); } @@ -152,7 +152,10 @@ static void qede_free_arfs_filter(struct qede_dev *edev, struct qede_arfs_fltr_node *fltr) { kfree(fltr->data); - clear_bit(fltr->sw_id, edev->arfs->arfs_fltr_bmap); + + if (fltr->sw_id < QEDE_RFS_MAX_FLTR) + clear_bit(fltr->sw_id, edev->arfs->arfs_fltr_bmap); + kfree(fltr); } @@ -214,7 +217,7 @@ void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc) if (fw_rc) { DP_NOTICE(edev, - "Failed arfs filter configuration fw_rc=%d, flow_id=%d, sw_id=%d, src_port=%d, dst_port=%d, rxq=%d\n", + "Failed arfs filter configuration fw_rc=%d, flow_id=%d, sw_id=0x%llx, src_port=%d, dst_port=%d, rxq=%d\n", fw_rc, fltr->flow_id, fltr->sw_id, ntohs(fltr->tuple.src_port), ntohs(fltr->tuple.dst_port), fltr->rxq_id); @@ -1348,7 +1351,7 @@ void qede_config_rx_mode(struct net_device *ndev) } static struct qede_arfs_fltr_node * -qede_get_arfs_fltr_by_loc(struct hlist_head *head, u32 location) +qede_get_arfs_fltr_by_loc(struct hlist_head *head, u64 location) { struct qede_arfs_fltr_node *fltr; @@ -1959,9 +1962,8 @@ int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) return rc; } -int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) +int qede_delete_flow_filter(struct qede_dev *edev, u64 cookie) { - struct ethtool_rx_flow_spec *fsp = &info->fs; struct qede_arfs_fltr_node *fltr = NULL; int rc = -EPERM; @@ -1970,7 +1972,7 @@ int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) goto unlock; fltr = qede_get_arfs_fltr_by_loc(QEDE_ARFS_BUCKET_HEAD(edev, 0), - fsp->location); + cookie); if (!fltr) goto unlock; @@ -2000,3 +2002,293 @@ int qede_get_arfs_filter_count(struct qede_dev *edev) __qede_unlock(edev); return count; } + +static int qede_parse_actions(struct qede_dev *edev, + struct tcf_exts *exts) +{ + int rc = -EINVAL, num_act = 0; + const struct tc_action *a; + bool is_drop = false; + LIST_HEAD(actions); + + if (!tcf_exts_has_actions(exts)) { + DP_NOTICE(edev, "No tc actions received\n"); + return rc; + } + + tcf_exts_to_list(exts, &actions); + list_for_each_entry(a, &actions, list) { + num_act++; + + if (is_tcf_gact_shot(a)) + is_drop = true; + } + + if (num_act == 1 && is_drop) + return 0; + + return rc; +} + +static int +qede_tc_parse_ports(struct qede_dev *edev, + struct tc_cls_flower_offload *f, + struct qede_arfs_tuple *t) +{ + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_dissector_key_ports *key, *mask; + + key = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_PORTS, + f->key); + mask = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_PORTS, + f->mask); + + if ((key->src && mask->src != U16_MAX) || + (key->dst && mask->dst != U16_MAX)) { + DP_NOTICE(edev, "Do not support ports masks\n"); + return -EINVAL; + } + + t->src_port = key->src; + t->dst_port = key->dst; + } + + return 0; +} + +static int +qede_tc_parse_v6_common(struct qede_dev *edev, + struct tc_cls_flower_offload *f, + struct qede_arfs_tuple *t) +{ + struct in6_addr zero_addr, addr; + + memset(&zero_addr, 0, sizeof(addr)); + memset(&addr, 0xff, sizeof(addr)); + + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { + struct flow_dissector_key_ipv6_addrs *key, *mask; + + key = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_IPV6_ADDRS, + f->key); + mask = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_IPV6_ADDRS, + f->mask); + + if ((memcmp(&key->src, &zero_addr, sizeof(addr)) && + memcmp(&mask->src, &addr, sizeof(addr))) || + (memcmp(&key->dst, &zero_addr, sizeof(addr)) && + memcmp(&mask->dst, &addr, sizeof(addr)))) { + DP_NOTICE(edev, + "Do not support IPv6 address prefix/mask\n"); + return -EINVAL; + } + + memcpy(&t->src_ipv6, &key->src, sizeof(addr)); + memcpy(&t->dst_ipv6, &key->dst, sizeof(addr)); + } + + if (qede_tc_parse_ports(edev, f, t)) + return -EINVAL; + + return qede_set_v6_tuple_to_profile(edev, t, &zero_addr); +} + +static int +qede_tc_parse_v4_common(struct qede_dev *edev, + struct tc_cls_flower_offload *f, + struct qede_arfs_tuple *t) +{ + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + struct flow_dissector_key_ipv4_addrs *key, *mask; + + key = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_IPV4_ADDRS, + f->key); + mask = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_IPV4_ADDRS, + f->mask); + + if ((key->src && mask->src != U32_MAX) || + (key->dst && mask->dst != U32_MAX)) { + DP_NOTICE(edev, "Do not support ipv4 prefix/masks\n"); + return -EINVAL; + } + + t->src_ipv4 = key->src; + t->dst_ipv4 = key->dst; + } + + if (qede_tc_parse_ports(edev, f, t)) + return -EINVAL; + + return qede_set_v4_tuple_to_profile(edev, t); +} + +static int +qede_tc_parse_tcp_v6(struct qede_dev *edev, + struct tc_cls_flower_offload *f, + struct qede_arfs_tuple *tuple) +{ + tuple->ip_proto = IPPROTO_TCP; + tuple->eth_proto = htons(ETH_P_IPV6); + + return qede_tc_parse_v6_common(edev, f, tuple); +} + +static int +qede_tc_parse_tcp_v4(struct qede_dev *edev, + struct tc_cls_flower_offload *f, + struct qede_arfs_tuple *tuple) +{ + tuple->ip_proto = IPPROTO_TCP; + tuple->eth_proto = htons(ETH_P_IP); + + return qede_tc_parse_v4_common(edev, f, tuple); +} + +static int +qede_tc_parse_udp_v6(struct qede_dev *edev, + struct tc_cls_flower_offload *f, + struct qede_arfs_tuple *tuple) +{ + tuple->ip_proto = IPPROTO_UDP; + tuple->eth_proto = htons(ETH_P_IPV6); + + return qede_tc_parse_v6_common(edev, f, tuple); +} + +static int +qede_tc_parse_udp_v4(struct qede_dev *edev, + struct tc_cls_flower_offload *f, + struct qede_arfs_tuple *tuple) +{ + tuple->ip_proto = IPPROTO_UDP; + tuple->eth_proto = htons(ETH_P_IP); + + return qede_tc_parse_v4_common(edev, f, tuple); +} + +static int +qede_parse_flower_attr(struct qede_dev *edev, __be16 proto, + struct tc_cls_flower_offload *f, + struct qede_arfs_tuple *tuple) +{ + int rc = -EINVAL; + u8 ip_proto = 0; + + memset(tuple, 0, sizeof(*tuple)); + + if (f->dissector->used_keys & + ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | + BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_BASIC) | + BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_PORTS))) { + DP_NOTICE(edev, "Unsupported key set:0x%x\n", + f->dissector->used_keys); + return -EOPNOTSUPP; + } + + if (proto != htons(ETH_P_IP) && + proto != htons(ETH_P_IPV6)) { + DP_NOTICE(edev, "Unsupported proto=0x%x\n", proto); + return -EPROTONOSUPPORT; + } + + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_dissector_key_basic *key; + + key = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_BASIC, + f->key); + ip_proto = key->ip_proto; + } + + if (ip_proto == IPPROTO_TCP && proto == htons(ETH_P_IP)) + rc = qede_tc_parse_tcp_v4(edev, f, tuple); + else if (ip_proto == IPPROTO_TCP && proto == htons(ETH_P_IPV6)) + rc = qede_tc_parse_tcp_v6(edev, f, tuple); + else if (ip_proto == IPPROTO_UDP && proto == htons(ETH_P_IP)) + rc = qede_tc_parse_udp_v4(edev, f, tuple); + else if (ip_proto == IPPROTO_UDP && proto == htons(ETH_P_IPV6)) + rc = qede_tc_parse_udp_v6(edev, f, tuple); + else + DP_NOTICE(edev, "Invalid tc protocol request\n"); + + return rc; +} + +int qede_add_tc_flower_fltr(struct qede_dev *edev, __be16 proto, + struct tc_cls_flower_offload *f) +{ + struct qede_arfs_fltr_node *n; + int min_hlen, rc = -EINVAL; + struct qede_arfs_tuple t; + + __qede_lock(edev); + + if (!edev->arfs) { + rc = -EPERM; + goto unlock; + } + + /* parse flower attribute and prepare filter */ + if (qede_parse_flower_attr(edev, proto, f, &t)) + goto unlock; + + /* Validate profile mode and number of filters */ + if ((edev->arfs->filter_count && edev->arfs->mode != t.mode) || + edev->arfs->filter_count == QEDE_RFS_MAX_FLTR) { + DP_NOTICE(edev, + "Filter configuration invalidated, filter mode=0x%x, configured mode=0x%x, filter count=0x%x\n", + t.mode, edev->arfs->mode, edev->arfs->filter_count); + goto unlock; + } + + /* parse tc actions and get the vf_id */ + if (qede_parse_actions(edev, f->exts)) + goto unlock; + + if (qede_flow_find_fltr(edev, &t)) { + rc = -EEXIST; + goto unlock; + } + + n = kzalloc(sizeof(*n), GFP_KERNEL); + if (!n) { + rc = -ENOMEM; + goto unlock; + } + + min_hlen = qede_flow_get_min_header_size(&t); + + n->data = kzalloc(min_hlen, GFP_KERNEL); + if (!n->data) { + kfree(n); + rc = -ENOMEM; + goto unlock; + } + + memcpy(&n->tuple, &t, sizeof(n->tuple)); + + n->buf_len = min_hlen; + n->b_is_drop = true; + n->sw_id = f->cookie; + + n->tuple.build_hdr(&n->tuple, n->data); + + rc = qede_enqueue_fltr_and_config_searcher(edev, n, 0); + if (rc) + goto unlock; + + qede_configure_arfs_fltr(edev, n, n->rxq_id, true); + rc = qede_poll_arfs_filter_config(edev, n); + +unlock: + __qede_unlock(edev); + return rc; +} diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index d7299afa902c..4b5d98f60d32 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -556,13 +556,67 @@ int qede_setup_tc(struct net_device *ndev, u8 num_tc) return 0; } +static int +qede_set_flower(struct qede_dev *edev, struct tc_cls_flower_offload *f, + __be16 proto) +{ + switch (f->command) { + case TC_CLSFLOWER_REPLACE: + return qede_add_tc_flower_fltr(edev, proto, f); + case TC_CLSFLOWER_DESTROY: + return qede_delete_flow_filter(edev, f->cookie); + default: + return -EOPNOTSUPP; + } +} + +static int qede_setup_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv) +{ + struct tc_cls_flower_offload *f; + struct qede_dev *edev = cb_priv; + + if (!tc_cls_can_offload_and_chain0(edev->ndev, type_data)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_CLSFLOWER: + f = type_data; + return qede_set_flower(edev, f, f->common.protocol); + default: + return -EOPNOTSUPP; + } +} + +static int qede_setup_tc_block(struct qede_dev *edev, + struct tc_block_offload *f) +{ + if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS) + return -EOPNOTSUPP; + + switch (f->command) { + case TC_BLOCK_BIND: + return tcf_block_cb_register(f->block, + qede_setup_tc_block_cb, + edev, edev, f->extack); + case TC_BLOCK_UNBIND: + tcf_block_cb_unregister(f->block, qede_setup_tc_block_cb, edev); + return 0; + default: + return -EOPNOTSUPP; + } +} + static int qede_setup_tc_offload(struct net_device *dev, enum tc_setup_type type, void *type_data) { + struct qede_dev *edev = netdev_priv(dev); struct tc_mqprio_qopt *mqprio; switch (type) { + case TC_SETUP_BLOCK: + return qede_setup_tc_block(edev, type_data); case TC_SETUP_QDISC_MQPRIO: mqprio = type_data; @@ -727,7 +781,7 @@ static void qede_init_ndev(struct qede_dev *edev) /* user-changeble features */ hw_features = NETIF_F_GRO | NETIF_F_GRO_HW | NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_TSO | NETIF_F_TSO6; + NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_TC; if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) hw_features |= NETIF_F_NTUPLE;