net: hns3: add support to modify tqps number
authorPeng Li <lipeng321@huawei.com>
Fri, 22 Dec 2017 04:21:41 +0000 (12:21 +0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 27 Dec 2017 15:40:20 +0000 (10:40 -0500)
This patch adds the support to change tqps number for PF driver
by using ehtool -L command.

Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Mingguang Qu <qumingguang@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c

index d8877215723d4862ed0201b45dd0480d356b488b..a5d3d2272ac84e7494dd6df667d6aa5025a35a89 100644 (file)
@@ -388,6 +388,9 @@ struct hnae3_ae_ops {
                            enum hnae3_reset_type reset);
        void (*get_channels)(struct hnae3_handle *handle,
                             struct ethtool_channels *ch);
+       void (*get_tqps_and_rss_info)(struct hnae3_handle *h,
+                                     u16 *free_tqps, u16 *max_rss_size);
+       int (*set_channels)(struct hnae3_handle *handle, u32 new_tqps_num);
 };
 
 struct hnae3_dcb_ops {
index c2c13238db526c43e67793c925c3c0b71cdc2f97..7e92068f26550971815595fc3b570f6cec4328ef 100644 (file)
@@ -2651,6 +2651,19 @@ err:
        return ret;
 }
 
+static void hns3_put_ring_config(struct hns3_nic_priv *priv)
+{
+       struct hnae3_handle *h = priv->ae_handle;
+       int i;
+
+       for (i = 0; i < h->kinfo.num_tqps; i++) {
+               devm_kfree(priv->dev, priv->ring_data[i].ring);
+               devm_kfree(priv->dev,
+                          priv->ring_data[i + h->kinfo.num_tqps].ring);
+       }
+       devm_kfree(priv->dev, priv->ring_data);
+}
+
 static int hns3_alloc_ring_memory(struct hns3_enet_ring *ring)
 {
        int ret;
@@ -3162,6 +3175,115 @@ static int hns3_reset_notify(struct hnae3_handle *handle,
        return ret;
 }
 
+static u16 hns3_get_max_available_channels(struct net_device *netdev)
+{
+       struct hnae3_handle *h = hns3_get_handle(netdev);
+       u16 free_tqps, max_rss_size, max_tqps;
+
+       h->ae_algo->ops->get_tqps_and_rss_info(h, &free_tqps, &max_rss_size);
+       max_tqps = h->kinfo.num_tc * max_rss_size;
+
+       return min_t(u16, max_tqps, (free_tqps + h->kinfo.num_tqps));
+}
+
+static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num)
+{
+       struct hns3_nic_priv *priv = netdev_priv(netdev);
+       struct hnae3_handle *h = hns3_get_handle(netdev);
+       int ret;
+
+       ret = h->ae_algo->ops->set_channels(h, new_tqp_num);
+       if (ret)
+               return ret;
+
+       ret = hns3_get_ring_config(priv);
+       if (ret)
+               return ret;
+
+       ret = hns3_nic_init_vector_data(priv);
+       if (ret)
+               goto err_uninit_vector;
+
+       ret = hns3_init_all_ring(priv);
+       if (ret)
+               goto err_put_ring;
+
+       return 0;
+
+err_put_ring:
+       hns3_put_ring_config(priv);
+err_uninit_vector:
+       hns3_nic_uninit_vector_data(priv);
+       return ret;
+}
+
+static int hns3_adjust_tqps_num(u8 num_tc, u32 new_tqp_num)
+{
+       return (new_tqp_num / num_tc) * num_tc;
+}
+
+int hns3_set_channels(struct net_device *netdev,
+                     struct ethtool_channels *ch)
+{
+       struct hns3_nic_priv *priv = netdev_priv(netdev);
+       struct hnae3_handle *h = hns3_get_handle(netdev);
+       struct hnae3_knic_private_info *kinfo = &h->kinfo;
+       bool if_running = netif_running(netdev);
+       u32 new_tqp_num = ch->combined_count;
+       u16 org_tqp_num;
+       int ret;
+
+       if (ch->rx_count || ch->tx_count)
+               return -EINVAL;
+
+       if (new_tqp_num > hns3_get_max_available_channels(netdev) ||
+           new_tqp_num < kinfo->num_tc) {
+               dev_err(&netdev->dev,
+                       "Change tqps fail, the tqp range is from %d to %d",
+                       kinfo->num_tc,
+                       hns3_get_max_available_channels(netdev));
+               return -EINVAL;
+       }
+
+       new_tqp_num = hns3_adjust_tqps_num(kinfo->num_tc, new_tqp_num);
+       if (kinfo->num_tqps == new_tqp_num)
+               return 0;
+
+       if (if_running)
+               dev_close(netdev);
+
+       hns3_clear_all_ring(h);
+
+       ret = hns3_nic_uninit_vector_data(priv);
+       if (ret) {
+               dev_err(&netdev->dev,
+                       "Unbind vector with tqp fail, nothing is changed");
+               goto open_netdev;
+       }
+
+       hns3_uninit_all_ring(priv);
+
+       org_tqp_num = h->kinfo.num_tqps;
+       ret = hns3_modify_tqp_num(netdev, new_tqp_num);
+       if (ret) {
+               ret = hns3_modify_tqp_num(netdev, org_tqp_num);
+               if (ret) {
+                       /* If revert to old tqp failed, fatal error occurred */
+                       dev_err(&netdev->dev,
+                               "Revert to old tqp num fail, ret=%d", ret);
+                       return ret;
+               }
+               dev_info(&netdev->dev,
+                        "Change tqp num fail, Revert to old tqp num");
+       }
+
+open_netdev:
+       if (if_running)
+               dev_open(netdev);
+
+       return ret;
+}
+
 static const struct hnae3_client_ops client_ops = {
        .init_instance = hns3_client_init,
        .uninit_instance = hns3_client_uninit,
index 8a9de759957bb694ceff04f57fc2822f29865b6f..a2a7ea3e9a3a2bd2a8c4faf5dc8c070fa3502c51 100644 (file)
@@ -595,6 +595,8 @@ static inline void hns3_write_reg(void __iomem *base, u32 reg, u32 value)
        (((struct hns3_nic_priv *)netdev_priv(ndev))->ae_handle)
 
 void hns3_ethtool_set_ops(struct net_device *netdev);
+int hns3_set_channels(struct net_device *netdev,
+                     struct ethtool_channels *ch);
 
 bool hns3_clean_tx_ring(struct hns3_enet_ring *ring, int budget);
 int hns3_init_all_ring(struct hns3_nic_priv *priv);
index 23af36c2156881feaaa2366401a5d58ac0d42cd9..1b2d79bf1fd2cd859fac21b4ff1790e4aaf97d9a 100644 (file)
@@ -893,6 +893,7 @@ static const struct ethtool_ops hns3_ethtool_ops = {
        .set_link_ksettings = hns3_set_link_ksettings,
        .nway_reset = hns3_nway_reset,
        .get_channels = hns3_get_channels,
+       .set_channels = hns3_set_channels,
 };
 
 void hns3_ethtool_set_ops(struct net_device *netdev)
index a3101bcc33dd017351f9a20ae10a49295647040a..7fab1024c9f86fe269259aba8955d3e2163677e6 100644 (file)
@@ -5022,6 +5022,116 @@ static void hclge_get_channels(struct hnae3_handle *handle,
        ch->combined_count = vport->alloc_tqps;
 }
 
+static void hclge_get_tqps_and_rss_info(struct hnae3_handle *handle,
+                                       u16 *free_tqps, u16 *max_rss_size)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+       u16 temp_tqps = 0;
+       int i;
+
+       for (i = 0; i < hdev->num_tqps; i++) {
+               if (!hdev->htqp[i].alloced)
+                       temp_tqps++;
+       }
+       *free_tqps = temp_tqps;
+       *max_rss_size = hdev->rss_size_max;
+}
+
+static void hclge_release_tqp(struct hclge_vport *vport)
+{
+       struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo;
+       struct hclge_dev *hdev = vport->back;
+       int i;
+
+       for (i = 0; i < kinfo->num_tqps; i++) {
+               struct hclge_tqp *tqp =
+                       container_of(kinfo->tqp[i], struct hclge_tqp, q);
+
+               tqp->q.handle = NULL;
+               tqp->q.tqp_index = 0;
+               tqp->alloced = false;
+       }
+
+       devm_kfree(&hdev->pdev->dev, kinfo->tqp);
+       kinfo->tqp = NULL;
+}
+
+static int hclge_set_channels(struct hnae3_handle *handle, u32 new_tqps_num)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo;
+       struct hclge_dev *hdev = vport->back;
+       int cur_rss_size = kinfo->rss_size;
+       int cur_tqps = kinfo->num_tqps;
+       u16 tc_offset[HCLGE_MAX_TC_NUM];
+       u16 tc_valid[HCLGE_MAX_TC_NUM];
+       u16 tc_size[HCLGE_MAX_TC_NUM];
+       u16 roundup_size;
+       u32 *rss_indir;
+       int ret, i;
+
+       hclge_release_tqp(vport);
+
+       ret = hclge_knic_setup(vport, new_tqps_num);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "setup nic fail, ret =%d\n", ret);
+               return ret;
+       }
+
+       ret = hclge_map_tqp_to_vport(hdev, vport);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "map vport tqp fail, ret =%d\n", ret);
+               return ret;
+       }
+
+       ret = hclge_tm_schd_init(hdev);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "tm schd init fail, ret =%d\n", ret);
+               return ret;
+       }
+
+       roundup_size = roundup_pow_of_two(kinfo->rss_size);
+       roundup_size = ilog2(roundup_size);
+       /* Set the RSS TC mode according to the new RSS size */
+       for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
+               tc_valid[i] = 0;
+
+               if (!(hdev->hw_tc_map & BIT(i)))
+                       continue;
+
+               tc_valid[i] = 1;
+               tc_size[i] = roundup_size;
+               tc_offset[i] = kinfo->rss_size * i;
+       }
+       ret = hclge_set_rss_tc_mode(hdev, tc_valid, tc_size, tc_offset);
+       if (ret)
+               return ret;
+
+       /* Reinitializes the rss indirect table according to the new RSS size */
+       rss_indir = kcalloc(HCLGE_RSS_IND_TBL_SIZE, sizeof(u32), GFP_KERNEL);
+       if (!rss_indir)
+               return -ENOMEM;
+
+       for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++)
+               rss_indir[i] = i % kinfo->rss_size;
+
+       ret = hclge_set_rss(handle, rss_indir, NULL, 0);
+       if (ret)
+               dev_err(&hdev->pdev->dev, "set rss indir table fail, ret=%d\n",
+                       ret);
+
+       kfree(rss_indir);
+
+       if (!ret)
+               dev_info(&hdev->pdev->dev,
+                        "Channels changed, rss_size from %d to %d, tqps from %d to %d",
+                        cur_rss_size, kinfo->rss_size,
+                        cur_tqps, kinfo->rss_size * kinfo->num_tc);
+
+       return ret;
+}
+
 static const struct hnae3_ae_ops hclge_ops = {
        .init_ae_dev = hclge_init_ae_dev,
        .uninit_ae_dev = hclge_uninit_ae_dev,
@@ -5066,6 +5176,8 @@ static const struct hnae3_ae_ops hclge_ops = {
        .set_vlan_filter = hclge_set_port_vlan_filter,
        .set_vf_vlan_filter = hclge_set_vf_vlan_filter,
        .reset_event = hclge_reset_event,
+       .get_tqps_and_rss_info = hclge_get_tqps_and_rss_info,
+       .set_channels = hclge_set_channels,
        .get_channels = hclge_get_channels,
 };