Commit a0337c0d authored by Luo bin's avatar Luo bin Committed by David S. Miller
Browse files

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: default avatarLuo bin <luobin9@huawei.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ea256222
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -49,6 +49,12 @@ enum hinic_rss_hash_type {
	HINIC_RSS_HASH_ENGINE_TYPE_MAX,
};

struct hinic_intr_coal_info {
	u8	pending_limt;
	u8	coalesce_timer_cfg;
	u8	resend_timer_cfg;
};

struct hinic_dev {
	struct net_device               *netdev;
	struct hinic_hwdev              *hwdev;
@@ -82,6 +88,8 @@ struct hinic_dev {
	struct hinic_rss_type		rss_type;
	u8				*rss_hkey_user;
	s32				*rss_indir_user;
	struct hinic_intr_coal_info	*rx_intr_coalesce;
	struct hinic_intr_coal_info	*tx_intr_coalesce;
	struct hinic_sriov_info sriov_info;
};

+234 −0
Original line number Diff line number Diff line
@@ -49,6 +49,13 @@
#define ETHTOOL_ADD_ADVERTISED_LINK_MODE(ecmd, mode)	\
				((ecmd)->advertising |= ADVERTISED_##mode)

#define COALESCE_PENDING_LIMIT_UNIT	8
#define	COALESCE_TIMER_CFG_UNIT		9
#define COALESCE_ALL_QUEUE		0xFFFF
#define COALESCE_MAX_PENDING_LIMIT	(255 * COALESCE_PENDING_LIMIT_UNIT)
#define COALESCE_MAX_TIMER_CFG		(255 * COALESCE_TIMER_CFG_UNIT)
#define OBJ_STR_MAX_LEN			32

struct hw2ethtool_link_mode {
	enum ethtool_link_mode_bit_indices link_mode_bit;
	u32 speed;
@@ -614,6 +621,215 @@ static int hinic_set_ringparam(struct net_device *netdev,
	return 0;
}

static int __hinic_get_coalesce(struct net_device *netdev,
				struct ethtool_coalesce *coal, u16 queue)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	struct hinic_intr_coal_info *rx_intr_coal_info;
	struct hinic_intr_coal_info *tx_intr_coal_info;

	if (queue == COALESCE_ALL_QUEUE) {
		/* get tx/rx irq0 as default parameters */
		rx_intr_coal_info = &nic_dev->rx_intr_coalesce[0];
		tx_intr_coal_info = &nic_dev->tx_intr_coalesce[0];
	} else {
		if (queue >= nic_dev->num_qps) {
			netif_err(nic_dev, drv, netdev,
				  "Invalid queue_id: %d\n", queue);
			return -EINVAL;
		}
		rx_intr_coal_info = &nic_dev->rx_intr_coalesce[queue];
		tx_intr_coal_info = &nic_dev->tx_intr_coalesce[queue];
	}

	/* coalesce_timer is in unit of 9us */
	coal->rx_coalesce_usecs = rx_intr_coal_info->coalesce_timer_cfg *
			COALESCE_TIMER_CFG_UNIT;
	/* coalesced_frames is in unit of 8 */
	coal->rx_max_coalesced_frames = rx_intr_coal_info->pending_limt *
			COALESCE_PENDING_LIMIT_UNIT;
	coal->tx_coalesce_usecs = tx_intr_coal_info->coalesce_timer_cfg *
			COALESCE_TIMER_CFG_UNIT;
	coal->tx_max_coalesced_frames = tx_intr_coal_info->pending_limt *
			COALESCE_PENDING_LIMIT_UNIT;

	return 0;
}

static int is_coalesce_exceed_limit(const struct ethtool_coalesce *coal)
{
	if (coal->rx_coalesce_usecs > COALESCE_MAX_TIMER_CFG ||
	    coal->rx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT ||
	    coal->tx_coalesce_usecs > COALESCE_MAX_TIMER_CFG ||
	    coal->tx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT)
		return -ERANGE;

	return 0;
}

static int set_queue_coalesce(struct hinic_dev *nic_dev, u16 q_id,
			      struct hinic_intr_coal_info *coal,
			      bool set_rx_coal)
{
	struct hinic_intr_coal_info *intr_coal = NULL;
	struct hinic_msix_config interrupt_info = {0};
	struct net_device *netdev = nic_dev->netdev;
	u16 msix_idx;
	int err;

	intr_coal = set_rx_coal ? &nic_dev->rx_intr_coalesce[q_id] :
		    &nic_dev->tx_intr_coalesce[q_id];

	intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg;
	intr_coal->pending_limt = coal->pending_limt;

	/* netdev not running or qp not in using,
	 * don't need to set coalesce to hw
	 */
	if (!(nic_dev->flags & HINIC_INTF_UP) ||
	    q_id >= nic_dev->num_qps)
		return 0;

	msix_idx = set_rx_coal ? nic_dev->rxqs[q_id].rq->msix_entry :
		   nic_dev->txqs[q_id].sq->msix_entry;
	interrupt_info.msix_index = msix_idx;
	interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg;
	interrupt_info.pending_cnt = intr_coal->pending_limt;
	interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg;

	err = hinic_set_interrupt_cfg(nic_dev->hwdev, &interrupt_info);
	if (err)
		netif_warn(nic_dev, drv, netdev,
			   "Failed to set %s queue%d coalesce",
			   set_rx_coal ? "rx" : "tx", q_id);

	return err;
}

static int __set_hw_coal_param(struct hinic_dev *nic_dev,
			       struct hinic_intr_coal_info *intr_coal,
			       u16 queue, bool set_rx_coal)
{
	int err;
	u16 i;

	if (queue == COALESCE_ALL_QUEUE) {
		for (i = 0; i < nic_dev->max_qps; i++) {
			err = set_queue_coalesce(nic_dev, i, intr_coal,
						 set_rx_coal);
			if (err)
				return err;
		}
	} else {
		if (queue >= nic_dev->num_qps) {
			netif_err(nic_dev, drv, nic_dev->netdev,
				  "Invalid queue_id: %d\n", queue);
			return -EINVAL;
		}
		err = set_queue_coalesce(nic_dev, queue, intr_coal,
					 set_rx_coal);
		if (err)
			return err;
	}

	return 0;
}

static int __hinic_set_coalesce(struct net_device *netdev,
				struct ethtool_coalesce *coal, u16 queue)
{
	struct hinic_intr_coal_info *ori_rx_intr_coal = NULL;
	struct hinic_intr_coal_info *ori_tx_intr_coal = NULL;
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	struct hinic_intr_coal_info rx_intr_coal = {0};
	struct hinic_intr_coal_info tx_intr_coal = {0};
	char obj_str[OBJ_STR_MAX_LEN] = {0};
	bool set_rx_coal = false;
	bool set_tx_coal = false;
	int err;

	err = is_coalesce_exceed_limit(coal);
	if (err)
		return err;

	if (coal->rx_coalesce_usecs || coal->rx_max_coalesced_frames) {
		rx_intr_coal.coalesce_timer_cfg =
		(u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
		rx_intr_coal.pending_limt = (u8)(coal->rx_max_coalesced_frames /
				COALESCE_PENDING_LIMIT_UNIT);
		set_rx_coal = true;
	}

	if (coal->tx_coalesce_usecs || coal->tx_max_coalesced_frames) {
		tx_intr_coal.coalesce_timer_cfg =
		(u8)(coal->tx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
		tx_intr_coal.pending_limt = (u8)(coal->tx_max_coalesced_frames /
		COALESCE_PENDING_LIMIT_UNIT);
		set_tx_coal = true;
	}

	if (queue == COALESCE_ALL_QUEUE) {
		ori_rx_intr_coal = &nic_dev->rx_intr_coalesce[0];
		ori_tx_intr_coal = &nic_dev->tx_intr_coalesce[0];
		err = snprintf(obj_str, OBJ_STR_MAX_LEN, "for netdev");
	} else {
		ori_rx_intr_coal = &nic_dev->rx_intr_coalesce[queue];
		ori_tx_intr_coal = &nic_dev->tx_intr_coalesce[queue];
		err = snprintf(obj_str, OBJ_STR_MAX_LEN, "for queue %d", queue);
	}
	if (err <= 0 || err >= OBJ_STR_MAX_LEN) {
		netif_err(nic_dev, drv, netdev, "Failed to snprintf string, function return(%d) and dest_len(%d)\n",
			  err, OBJ_STR_MAX_LEN);
		return -EFAULT;
	}

	/* setting coalesce timer or pending limit to zero will disable
	 * coalesce
	 */
	if (set_rx_coal && (!rx_intr_coal.coalesce_timer_cfg ||
			    !rx_intr_coal.pending_limt))
		netif_warn(nic_dev, drv, netdev, "RX coalesce will be disabled\n");
	if (set_tx_coal && (!tx_intr_coal.coalesce_timer_cfg ||
			    !tx_intr_coal.pending_limt))
		netif_warn(nic_dev, drv, netdev, "TX coalesce will be disabled\n");

	if (set_rx_coal) {
		err = __set_hw_coal_param(nic_dev, &rx_intr_coal, queue, true);
		if (err)
			return err;
	}
	if (set_tx_coal) {
		err = __set_hw_coal_param(nic_dev, &tx_intr_coal, queue, false);
		if (err)
			return err;
	}
	return 0;
}

static int hinic_get_coalesce(struct net_device *netdev,
			      struct ethtool_coalesce *coal)
{
	return __hinic_get_coalesce(netdev, coal, COALESCE_ALL_QUEUE);
}

static int hinic_set_coalesce(struct net_device *netdev,
			      struct ethtool_coalesce *coal)
{
	return __hinic_set_coalesce(netdev, coal, COALESCE_ALL_QUEUE);
}

static int hinic_get_per_queue_coalesce(struct net_device *netdev, u32 queue,
					struct ethtool_coalesce *coal)
{
	return __hinic_get_coalesce(netdev, coal, queue);
}

static int hinic_set_per_queue_coalesce(struct net_device *netdev, u32 queue,
					struct ethtool_coalesce *coal)
{
	return __hinic_set_coalesce(netdev, coal, queue);
}

static void hinic_get_pauseparam(struct net_device *netdev,
				 struct ethtool_pauseparam *pause)
{
@@ -1293,12 +1509,21 @@ static void hinic_get_strings(struct net_device *netdev,
}

static const struct ethtool_ops hinic_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
				     ETHTOOL_COALESCE_RX_MAX_FRAMES |
				     ETHTOOL_COALESCE_TX_USECS |
				     ETHTOOL_COALESCE_TX_MAX_FRAMES,

	.get_link_ksettings = hinic_get_link_ksettings,
	.set_link_ksettings = hinic_set_link_ksettings,
	.get_drvinfo = hinic_get_drvinfo,
	.get_link = ethtool_op_get_link,
	.get_ringparam = hinic_get_ringparam,
	.set_ringparam = hinic_set_ringparam,
	.get_coalesce = hinic_get_coalesce,
	.set_coalesce = hinic_set_coalesce,
	.get_per_queue_coalesce = hinic_get_per_queue_coalesce,
	.set_per_queue_coalesce = hinic_set_per_queue_coalesce,
	.get_pauseparam = hinic_get_pauseparam,
	.set_pauseparam = hinic_set_pauseparam,
	.get_channels = hinic_get_channels,
@@ -1315,11 +1540,20 @@ static const struct ethtool_ops hinic_ethtool_ops = {
};

static const struct ethtool_ops hinicvf_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
				     ETHTOOL_COALESCE_RX_MAX_FRAMES |
				     ETHTOOL_COALESCE_TX_USECS |
				     ETHTOOL_COALESCE_TX_MAX_FRAMES,

	.get_link_ksettings = hinic_get_link_ksettings,
	.get_drvinfo = hinic_get_drvinfo,
	.get_link = ethtool_op_get_link,
	.get_ringparam = hinic_get_ringparam,
	.set_ringparam = hinic_set_ringparam,
	.get_coalesce = hinic_get_coalesce,
	.set_coalesce = hinic_set_coalesce,
	.get_per_queue_coalesce = hinic_get_per_queue_coalesce,
	.set_per_queue_coalesce = hinic_set_per_queue_coalesce,
	.get_channels = hinic_get_channels,
	.set_channels = hinic_set_channels,
	.get_rxnfc = hinic_get_rxnfc,
+62 −0
Original line number Diff line number Diff line
@@ -705,6 +705,68 @@ static int hinic_l2nic_reset(struct hinic_hwdev *hwdev)
	return 0;
}

int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
			    struct hinic_msix_config *interrupt_info)
{
	u16 out_size = sizeof(*interrupt_info);
	struct hinic_pfhwdev *pfhwdev;
	int err;

	if (!hwdev || !interrupt_info)
		return -EINVAL;

	pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);

	interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);

	err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
				HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,
				interrupt_info, sizeof(*interrupt_info),
				interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
	if (err || !out_size || interrupt_info->status) {
		dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
			err, interrupt_info->status, out_size);
		return -EIO;
	}

	return 0;
}

int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
			    struct hinic_msix_config *interrupt_info)
{
	u16 out_size = sizeof(*interrupt_info);
	struct hinic_msix_config temp_info;
	struct hinic_pfhwdev *pfhwdev;
	int err;

	if (!hwdev)
		return -EINVAL;

	pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);

	interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);

	err = hinic_get_interrupt_cfg(hwdev, &temp_info);
	if (err)
		return -EINVAL;

	interrupt_info->lli_credit_cnt = temp_info.lli_timer_cnt;
	interrupt_info->lli_timer_cnt = temp_info.lli_timer_cnt;

	err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
				HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
				interrupt_info, sizeof(*interrupt_info),
				interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
	if (err || !out_size || interrupt_info->status) {
		dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
			err, interrupt_info->status, out_size);
		return -EIO;
	}

	return 0;
}

/**
 * hinic_init_hwdev - Initialize the NIC HW
 * @pdev: the NIC pci device
+21 −0
Original line number Diff line number Diff line
@@ -285,6 +285,21 @@ struct hinic_cmd_l2nic_reset {
	u16	reset_flag;
};

struct hinic_msix_config {
	u8	status;
	u8	version;
	u8	rsvd0[6];

	u16	func_id;
	u16	msix_index;
	u8	pending_cnt;
	u8	coalesce_timer_cnt;
	u8	lli_timer_cnt;
	u8	lli_credit_cnt;
	u8	resend_timer_cnt;
	u8	rsvd1[3];
};

struct hinic_hwdev {
	struct hinic_hwif               *hwif;
	struct msix_entry               *msix_entries;
@@ -378,4 +393,10 @@ int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq,
void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index,
				enum hinic_msix_state flag);

int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
			    struct hinic_msix_config *interrupt_info);

int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
			    struct hinic_msix_config *interrupt_info);

#endif
+3 −0
Original line number Diff line number Diff line
@@ -78,6 +78,9 @@ enum hinic_comm_cmd {

	HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP = 0x33,

	HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
	HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,

	HINIC_COMM_CMD_L2NIC_RESET		= 0x4b,

	HINIC_COMM_CMD_PAGESIZE_SET	= 0x50,
Loading