Skip to content

Commit a0337c0

Browse files
Luo bindavem330
authored andcommitted
hinic: add support to set and get irq coalesce
add support to set TX/RX irq coalesce params with ethtool -C and get these params with ethtool -c. Signed-off-by: Luo bin <luobin9@huawei.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent ea25622 commit a0337c0

File tree

8 files changed

+418
-1
lines changed

8 files changed

+418
-1
lines changed

drivers/net/ethernet/huawei/hinic/hinic_dev.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ enum hinic_rss_hash_type {
4949
HINIC_RSS_HASH_ENGINE_TYPE_MAX,
5050
};
5151

52+
struct hinic_intr_coal_info {
53+
u8 pending_limt;
54+
u8 coalesce_timer_cfg;
55+
u8 resend_timer_cfg;
56+
};
57+
5258
struct hinic_dev {
5359
struct net_device *netdev;
5460
struct hinic_hwdev *hwdev;
@@ -82,6 +88,8 @@ struct hinic_dev {
8288
struct hinic_rss_type rss_type;
8389
u8 *rss_hkey_user;
8490
s32 *rss_indir_user;
91+
struct hinic_intr_coal_info *rx_intr_coalesce;
92+
struct hinic_intr_coal_info *tx_intr_coalesce;
8593
struct hinic_sriov_info sriov_info;
8694
};
8795

drivers/net/ethernet/huawei/hinic/hinic_ethtool.c

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@
4949
#define ETHTOOL_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \
5050
((ecmd)->advertising |= ADVERTISED_##mode)
5151

52+
#define COALESCE_PENDING_LIMIT_UNIT 8
53+
#define COALESCE_TIMER_CFG_UNIT 9
54+
#define COALESCE_ALL_QUEUE 0xFFFF
55+
#define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT)
56+
#define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT)
57+
#define OBJ_STR_MAX_LEN 32
58+
5259
struct hw2ethtool_link_mode {
5360
enum ethtool_link_mode_bit_indices link_mode_bit;
5461
u32 speed;
@@ -614,6 +621,215 @@ static int hinic_set_ringparam(struct net_device *netdev,
614621
return 0;
615622
}
616623

624+
static int __hinic_get_coalesce(struct net_device *netdev,
625+
struct ethtool_coalesce *coal, u16 queue)
626+
{
627+
struct hinic_dev *nic_dev = netdev_priv(netdev);
628+
struct hinic_intr_coal_info *rx_intr_coal_info;
629+
struct hinic_intr_coal_info *tx_intr_coal_info;
630+
631+
if (queue == COALESCE_ALL_QUEUE) {
632+
/* get tx/rx irq0 as default parameters */
633+
rx_intr_coal_info = &nic_dev->rx_intr_coalesce[0];
634+
tx_intr_coal_info = &nic_dev->tx_intr_coalesce[0];
635+
} else {
636+
if (queue >= nic_dev->num_qps) {
637+
netif_err(nic_dev, drv, netdev,
638+
"Invalid queue_id: %d\n", queue);
639+
return -EINVAL;
640+
}
641+
rx_intr_coal_info = &nic_dev->rx_intr_coalesce[queue];
642+
tx_intr_coal_info = &nic_dev->tx_intr_coalesce[queue];
643+
}
644+
645+
/* coalesce_timer is in unit of 9us */
646+
coal->rx_coalesce_usecs = rx_intr_coal_info->coalesce_timer_cfg *
647+
COALESCE_TIMER_CFG_UNIT;
648+
/* coalesced_frames is in unit of 8 */
649+
coal->rx_max_coalesced_frames = rx_intr_coal_info->pending_limt *
650+
COALESCE_PENDING_LIMIT_UNIT;
651+
coal->tx_coalesce_usecs = tx_intr_coal_info->coalesce_timer_cfg *
652+
COALESCE_TIMER_CFG_UNIT;
653+
coal->tx_max_coalesced_frames = tx_intr_coal_info->pending_limt *
654+
COALESCE_PENDING_LIMIT_UNIT;
655+
656+
return 0;
657+
}
658+
659+
static int is_coalesce_exceed_limit(const struct ethtool_coalesce *coal)
660+
{
661+
if (coal->rx_coalesce_usecs > COALESCE_MAX_TIMER_CFG ||
662+
coal->rx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT ||
663+
coal->tx_coalesce_usecs > COALESCE_MAX_TIMER_CFG ||
664+
coal->tx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT)
665+
return -ERANGE;
666+
667+
return 0;
668+
}
669+
670+
static int set_queue_coalesce(struct hinic_dev *nic_dev, u16 q_id,
671+
struct hinic_intr_coal_info *coal,
672+
bool set_rx_coal)
673+
{
674+
struct hinic_intr_coal_info *intr_coal = NULL;
675+
struct hinic_msix_config interrupt_info = {0};
676+
struct net_device *netdev = nic_dev->netdev;
677+
u16 msix_idx;
678+
int err;
679+
680+
intr_coal = set_rx_coal ? &nic_dev->rx_intr_coalesce[q_id] :
681+
&nic_dev->tx_intr_coalesce[q_id];
682+
683+
intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg;
684+
intr_coal->pending_limt = coal->pending_limt;
685+
686+
/* netdev not running or qp not in using,
687+
* don't need to set coalesce to hw
688+
*/
689+
if (!(nic_dev->flags & HINIC_INTF_UP) ||
690+
q_id >= nic_dev->num_qps)
691+
return 0;
692+
693+
msix_idx = set_rx_coal ? nic_dev->rxqs[q_id].rq->msix_entry :
694+
nic_dev->txqs[q_id].sq->msix_entry;
695+
interrupt_info.msix_index = msix_idx;
696+
interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg;
697+
interrupt_info.pending_cnt = intr_coal->pending_limt;
698+
interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg;
699+
700+
err = hinic_set_interrupt_cfg(nic_dev->hwdev, &interrupt_info);
701+
if (err)
702+
netif_warn(nic_dev, drv, netdev,
703+
"Failed to set %s queue%d coalesce",
704+
set_rx_coal ? "rx" : "tx", q_id);
705+
706+
return err;
707+
}
708+
709+
static int __set_hw_coal_param(struct hinic_dev *nic_dev,
710+
struct hinic_intr_coal_info *intr_coal,
711+
u16 queue, bool set_rx_coal)
712+
{
713+
int err;
714+
u16 i;
715+
716+
if (queue == COALESCE_ALL_QUEUE) {
717+
for (i = 0; i < nic_dev->max_qps; i++) {
718+
err = set_queue_coalesce(nic_dev, i, intr_coal,
719+
set_rx_coal);
720+
if (err)
721+
return err;
722+
}
723+
} else {
724+
if (queue >= nic_dev->num_qps) {
725+
netif_err(nic_dev, drv, nic_dev->netdev,
726+
"Invalid queue_id: %d\n", queue);
727+
return -EINVAL;
728+
}
729+
err = set_queue_coalesce(nic_dev, queue, intr_coal,
730+
set_rx_coal);
731+
if (err)
732+
return err;
733+
}
734+
735+
return 0;
736+
}
737+
738+
static int __hinic_set_coalesce(struct net_device *netdev,
739+
struct ethtool_coalesce *coal, u16 queue)
740+
{
741+
struct hinic_intr_coal_info *ori_rx_intr_coal = NULL;
742+
struct hinic_intr_coal_info *ori_tx_intr_coal = NULL;
743+
struct hinic_dev *nic_dev = netdev_priv(netdev);
744+
struct hinic_intr_coal_info rx_intr_coal = {0};
745+
struct hinic_intr_coal_info tx_intr_coal = {0};
746+
char obj_str[OBJ_STR_MAX_LEN] = {0};
747+
bool set_rx_coal = false;
748+
bool set_tx_coal = false;
749+
int err;
750+
751+
err = is_coalesce_exceed_limit(coal);
752+
if (err)
753+
return err;
754+
755+
if (coal->rx_coalesce_usecs || coal->rx_max_coalesced_frames) {
756+
rx_intr_coal.coalesce_timer_cfg =
757+
(u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
758+
rx_intr_coal.pending_limt = (u8)(coal->rx_max_coalesced_frames /
759+
COALESCE_PENDING_LIMIT_UNIT);
760+
set_rx_coal = true;
761+
}
762+
763+
if (coal->tx_coalesce_usecs || coal->tx_max_coalesced_frames) {
764+
tx_intr_coal.coalesce_timer_cfg =
765+
(u8)(coal->tx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
766+
tx_intr_coal.pending_limt = (u8)(coal->tx_max_coalesced_frames /
767+
COALESCE_PENDING_LIMIT_UNIT);
768+
set_tx_coal = true;
769+
}
770+
771+
if (queue == COALESCE_ALL_QUEUE) {
772+
ori_rx_intr_coal = &nic_dev->rx_intr_coalesce[0];
773+
ori_tx_intr_coal = &nic_dev->tx_intr_coalesce[0];
774+
err = snprintf(obj_str, OBJ_STR_MAX_LEN, "for netdev");
775+
} else {
776+
ori_rx_intr_coal = &nic_dev->rx_intr_coalesce[queue];
777+
ori_tx_intr_coal = &nic_dev->tx_intr_coalesce[queue];
778+
err = snprintf(obj_str, OBJ_STR_MAX_LEN, "for queue %d", queue);
779+
}
780+
if (err <= 0 || err >= OBJ_STR_MAX_LEN) {
781+
netif_err(nic_dev, drv, netdev, "Failed to snprintf string, function return(%d) and dest_len(%d)\n",
782+
err, OBJ_STR_MAX_LEN);
783+
return -EFAULT;
784+
}
785+
786+
/* setting coalesce timer or pending limit to zero will disable
787+
* coalesce
788+
*/
789+
if (set_rx_coal && (!rx_intr_coal.coalesce_timer_cfg ||
790+
!rx_intr_coal.pending_limt))
791+
netif_warn(nic_dev, drv, netdev, "RX coalesce will be disabled\n");
792+
if (set_tx_coal && (!tx_intr_coal.coalesce_timer_cfg ||
793+
!tx_intr_coal.pending_limt))
794+
netif_warn(nic_dev, drv, netdev, "TX coalesce will be disabled\n");
795+
796+
if (set_rx_coal) {
797+
err = __set_hw_coal_param(nic_dev, &rx_intr_coal, queue, true);
798+
if (err)
799+
return err;
800+
}
801+
if (set_tx_coal) {
802+
err = __set_hw_coal_param(nic_dev, &tx_intr_coal, queue, false);
803+
if (err)
804+
return err;
805+
}
806+
return 0;
807+
}
808+
809+
static int hinic_get_coalesce(struct net_device *netdev,
810+
struct ethtool_coalesce *coal)
811+
{
812+
return __hinic_get_coalesce(netdev, coal, COALESCE_ALL_QUEUE);
813+
}
814+
815+
static int hinic_set_coalesce(struct net_device *netdev,
816+
struct ethtool_coalesce *coal)
817+
{
818+
return __hinic_set_coalesce(netdev, coal, COALESCE_ALL_QUEUE);
819+
}
820+
821+
static int hinic_get_per_queue_coalesce(struct net_device *netdev, u32 queue,
822+
struct ethtool_coalesce *coal)
823+
{
824+
return __hinic_get_coalesce(netdev, coal, queue);
825+
}
826+
827+
static int hinic_set_per_queue_coalesce(struct net_device *netdev, u32 queue,
828+
struct ethtool_coalesce *coal)
829+
{
830+
return __hinic_set_coalesce(netdev, coal, queue);
831+
}
832+
617833
static void hinic_get_pauseparam(struct net_device *netdev,
618834
struct ethtool_pauseparam *pause)
619835
{
@@ -1293,12 +1509,21 @@ static void hinic_get_strings(struct net_device *netdev,
12931509
}
12941510

12951511
static const struct ethtool_ops hinic_ethtool_ops = {
1512+
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
1513+
ETHTOOL_COALESCE_RX_MAX_FRAMES |
1514+
ETHTOOL_COALESCE_TX_USECS |
1515+
ETHTOOL_COALESCE_TX_MAX_FRAMES,
1516+
12961517
.get_link_ksettings = hinic_get_link_ksettings,
12971518
.set_link_ksettings = hinic_set_link_ksettings,
12981519
.get_drvinfo = hinic_get_drvinfo,
12991520
.get_link = ethtool_op_get_link,
13001521
.get_ringparam = hinic_get_ringparam,
13011522
.set_ringparam = hinic_set_ringparam,
1523+
.get_coalesce = hinic_get_coalesce,
1524+
.set_coalesce = hinic_set_coalesce,
1525+
.get_per_queue_coalesce = hinic_get_per_queue_coalesce,
1526+
.set_per_queue_coalesce = hinic_set_per_queue_coalesce,
13021527
.get_pauseparam = hinic_get_pauseparam,
13031528
.set_pauseparam = hinic_set_pauseparam,
13041529
.get_channels = hinic_get_channels,
@@ -1315,11 +1540,20 @@ static const struct ethtool_ops hinic_ethtool_ops = {
13151540
};
13161541

13171542
static const struct ethtool_ops hinicvf_ethtool_ops = {
1543+
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
1544+
ETHTOOL_COALESCE_RX_MAX_FRAMES |
1545+
ETHTOOL_COALESCE_TX_USECS |
1546+
ETHTOOL_COALESCE_TX_MAX_FRAMES,
1547+
13181548
.get_link_ksettings = hinic_get_link_ksettings,
13191549
.get_drvinfo = hinic_get_drvinfo,
13201550
.get_link = ethtool_op_get_link,
13211551
.get_ringparam = hinic_get_ringparam,
13221552
.set_ringparam = hinic_set_ringparam,
1553+
.get_coalesce = hinic_get_coalesce,
1554+
.set_coalesce = hinic_set_coalesce,
1555+
.get_per_queue_coalesce = hinic_get_per_queue_coalesce,
1556+
.set_per_queue_coalesce = hinic_set_per_queue_coalesce,
13231557
.get_channels = hinic_get_channels,
13241558
.set_channels = hinic_set_channels,
13251559
.get_rxnfc = hinic_get_rxnfc,

drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,68 @@ static int hinic_l2nic_reset(struct hinic_hwdev *hwdev)
705705
return 0;
706706
}
707707

708+
int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
709+
struct hinic_msix_config *interrupt_info)
710+
{
711+
u16 out_size = sizeof(*interrupt_info);
712+
struct hinic_pfhwdev *pfhwdev;
713+
int err;
714+
715+
if (!hwdev || !interrupt_info)
716+
return -EINVAL;
717+
718+
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
719+
720+
interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
721+
722+
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
723+
HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,
724+
interrupt_info, sizeof(*interrupt_info),
725+
interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
726+
if (err || !out_size || interrupt_info->status) {
727+
dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
728+
err, interrupt_info->status, out_size);
729+
return -EIO;
730+
}
731+
732+
return 0;
733+
}
734+
735+
int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
736+
struct hinic_msix_config *interrupt_info)
737+
{
738+
u16 out_size = sizeof(*interrupt_info);
739+
struct hinic_msix_config temp_info;
740+
struct hinic_pfhwdev *pfhwdev;
741+
int err;
742+
743+
if (!hwdev)
744+
return -EINVAL;
745+
746+
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
747+
748+
interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
749+
750+
err = hinic_get_interrupt_cfg(hwdev, &temp_info);
751+
if (err)
752+
return -EINVAL;
753+
754+
interrupt_info->lli_credit_cnt = temp_info.lli_timer_cnt;
755+
interrupt_info->lli_timer_cnt = temp_info.lli_timer_cnt;
756+
757+
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
758+
HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
759+
interrupt_info, sizeof(*interrupt_info),
760+
interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
761+
if (err || !out_size || interrupt_info->status) {
762+
dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
763+
err, interrupt_info->status, out_size);
764+
return -EIO;
765+
}
766+
767+
return 0;
768+
}
769+
708770
/**
709771
* hinic_init_hwdev - Initialize the NIC HW
710772
* @pdev: the NIC pci device

drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,21 @@ struct hinic_cmd_l2nic_reset {
285285
u16 reset_flag;
286286
};
287287

288+
struct hinic_msix_config {
289+
u8 status;
290+
u8 version;
291+
u8 rsvd0[6];
292+
293+
u16 func_id;
294+
u16 msix_index;
295+
u8 pending_cnt;
296+
u8 coalesce_timer_cnt;
297+
u8 lli_timer_cnt;
298+
u8 lli_credit_cnt;
299+
u8 resend_timer_cnt;
300+
u8 rsvd1[3];
301+
};
302+
288303
struct hinic_hwdev {
289304
struct hinic_hwif *hwif;
290305
struct msix_entry *msix_entries;
@@ -378,4 +393,10 @@ int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq,
378393
void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index,
379394
enum hinic_msix_state flag);
380395

396+
int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
397+
struct hinic_msix_config *interrupt_info);
398+
399+
int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
400+
struct hinic_msix_config *interrupt_info);
401+
381402
#endif

0 commit comments

Comments
 (0)