net-next/hinic: Add logical Txq and Rxq
authorAviad Krawczyk <aviad.krawczyk@huawei.com>
Mon, 21 Aug 2017 15:55:56 +0000 (23:55 +0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 22 Aug 2017 17:48:53 +0000 (10:48 -0700)
Create the logical queues of the nic.

Signed-off-by: Aviad Krawczyk <aviad.krawczyk@huawei.com>
Signed-off-by: Zhao Chen <zhaochen6@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
12 files changed:
drivers/net/ethernet/huawei/hinic/Makefile
drivers/net/ethernet/huawei/hinic/hinic_dev.h
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
drivers/net/ethernet/huawei/hinic/hinic_hw_io.c [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_hw_io.h [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_main.c
drivers/net/ethernet/huawei/hinic/hinic_rx.c [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_rx.h [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_tx.c [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_tx.h [new file with mode: 0644]

index dbb1b9dbaa59851901a1bec0209fc4c6c8a12589..f60c449fbb4010a45026c8f990e03f09d61c98d2 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_HINIC) += hinic.o
 
-hinic-y := hinic_main.o hinic_port.o hinic_hw_dev.o hinic_hw_mgmt.o \
-          hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o
+hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \
+          hinic_hw_io.o hinic_hw_mgmt.o hinic_hw_api_cmd.o hinic_hw_eqs.o \
+          hinic_hw_if.o
index 5c5b4e974c569c46bc56cccf39070ba26a6a7547..5b8231dc3ff1890cf84c526d5cddc7060cd47c5e 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/bitops.h>
 
 #include "hinic_hw_dev.h"
+#include "hinic_tx.h"
+#include "hinic_rx.h"
 
 #define HINIC_DRV_NAME          "hinic"
 
@@ -49,6 +51,9 @@ struct hinic_dev {
 
        struct hinic_rx_mode_work       rx_mode_work;
        struct workqueue_struct         *workq;
+
+       struct hinic_txq                *txqs;
+       struct hinic_rxq                *rxqs;
 };
 
 #endif
index c3122b0307404f3d24e0e36b288c83b47dc3e6fb..5ae1c3682be0e4966bf52ad2c2efa3a6f485c787 100644 (file)
@@ -25,6 +25,8 @@
 #include "hinic_hw_if.h"
 #include "hinic_hw_eqs.h"
 #include "hinic_hw_mgmt.h"
+#include "hinic_hw_qp.h"
+#include "hinic_hw_io.h"
 #include "hinic_hw_dev.h"
 
 #define MAX_IRQS(max_qps, num_aeqs, num_ceqs)   \
@@ -229,6 +231,99 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd,
                                 HINIC_MGMT_MSG_SYNC);
 }
 
+/**
+ * get_base_qpn - get the first qp number
+ * @hwdev: the NIC HW device
+ * @base_qpn: returned qp number
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static int get_base_qpn(struct hinic_hwdev *hwdev, u16 *base_qpn)
+{
+       struct hinic_cmd_base_qpn cmd_base_qpn;
+       struct hinic_hwif *hwif = hwdev->hwif;
+       struct pci_dev *pdev = hwif->pdev;
+       u16 out_size;
+       int err;
+
+       cmd_base_qpn.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
+
+       err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_GLOBAL_QPN,
+                                &cmd_base_qpn, sizeof(cmd_base_qpn),
+                                &cmd_base_qpn, &out_size);
+       if (err || (out_size != sizeof(cmd_base_qpn)) || cmd_base_qpn.status) {
+               dev_err(&pdev->dev, "Failed to get base qpn, status = %d\n",
+                       cmd_base_qpn.status);
+               return -EFAULT;
+       }
+
+       *base_qpn = cmd_base_qpn.qpn;
+       return 0;
+}
+
+/**
+ * hinic_hwdev_ifup - Preparing the HW for passing IO
+ * @hwdev: the NIC HW device
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+int hinic_hwdev_ifup(struct hinic_hwdev *hwdev)
+{
+       struct hinic_func_to_io *func_to_io = &hwdev->func_to_io;
+       struct hinic_cap *nic_cap = &hwdev->nic_cap;
+       struct hinic_hwif *hwif = hwdev->hwif;
+       int err, num_aeqs, num_ceqs, num_qps;
+       struct msix_entry *sq_msix_entries;
+       struct msix_entry *rq_msix_entries;
+       struct pci_dev *pdev = hwif->pdev;
+       u16 base_qpn;
+
+       err = get_base_qpn(hwdev, &base_qpn);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to get global base qp number\n");
+               return err;
+       }
+
+       num_aeqs = HINIC_HWIF_NUM_AEQS(hwif);
+       num_ceqs = HINIC_HWIF_NUM_CEQS(hwif);
+       err = hinic_io_init(func_to_io, hwif, nic_cap->max_qps, 0, NULL);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to init IO channel\n");
+               return err;
+       }
+
+       num_qps = nic_cap->num_qps;
+       sq_msix_entries = &hwdev->msix_entries[num_aeqs + num_ceqs];
+       rq_msix_entries = &hwdev->msix_entries[num_aeqs + num_ceqs + num_qps];
+
+       err = hinic_io_create_qps(func_to_io, base_qpn, num_qps,
+                                 sq_msix_entries, rq_msix_entries);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to create QPs\n");
+               goto err_create_qps;
+       }
+
+       return 0;
+
+err_create_qps:
+       hinic_io_free(func_to_io);
+       return err;
+}
+
+/**
+ * hinic_hwdev_ifdown - Closing the HW for passing IO
+ * @hwdev: the NIC HW device
+ *
+ **/
+void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev)
+{
+       struct hinic_func_to_io *func_to_io = &hwdev->func_to_io;
+       struct hinic_cap *nic_cap = &hwdev->nic_cap;
+
+       hinic_io_destroy_qps(func_to_io, nic_cap->num_qps);
+       hinic_io_free(func_to_io);
+}
+
 /**
  * hinic_hwdev_cb_register - register callback handler for MGMT events
  * @hwdev: the NIC HW device
@@ -499,3 +594,39 @@ int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev)
 
        return nic_cap->num_qps;
 }
+
+/**
+ * hinic_hwdev_get_sq - get SQ
+ * @hwdev: the NIC HW device
+ * @i: the position of the SQ
+ *
+ * Return: the SQ in the i position
+ **/
+struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i)
+{
+       struct hinic_func_to_io *func_to_io = &hwdev->func_to_io;
+       struct hinic_qp *qp = &func_to_io->qps[i];
+
+       if (i >= hinic_hwdev_num_qps(hwdev))
+               return NULL;
+
+       return &qp->sq;
+}
+
+/**
+ * hinic_hwdev_get_sq - get RQ
+ * @hwdev: the NIC HW device
+ * @i: the position of the RQ
+ *
+ * Return: the RQ in the i position
+ **/
+struct hinic_rq *hinic_hwdev_get_rq(struct hinic_hwdev *hwdev, int i)
+{
+       struct hinic_func_to_io *func_to_io = &hwdev->func_to_io;
+       struct hinic_qp *qp = &func_to_io->qps[i];
+
+       if (i >= hinic_hwdev_num_qps(hwdev))
+               return NULL;
+
+       return &qp->rq;
+}
index 1cd8159766d41f08ef0746b543af2c804f640311..81c2c6e92898e4aa3c59b9167c63a91e2178d618 100644 (file)
@@ -23,6 +23,8 @@
 #include "hinic_hw_if.h"
 #include "hinic_hw_eqs.h"
 #include "hinic_hw_mgmt.h"
+#include "hinic_hw_qp.h"
+#include "hinic_hw_io.h"
 
 #define HINIC_MAX_QPS   32
 
@@ -72,11 +74,21 @@ enum hinic_cb_state {
        HINIC_CB_RUNNING = BIT(1),
 };
 
+struct hinic_cmd_base_qpn {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+
+       u16     func_idx;
+       u16     qpn;
+};
+
 struct hinic_hwdev {
        struct hinic_hwif               *hwif;
        struct msix_entry               *msix_entries;
 
        struct hinic_aeqs               aeqs;
+       struct hinic_func_to_io         func_to_io;
 
        struct hinic_cap                nic_cap;
 };
@@ -111,10 +123,18 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd,
                       void *buf_in, u16 in_size, void *buf_out,
                       u16 *out_size);
 
+int hinic_hwdev_ifup(struct hinic_hwdev *hwdev);
+
+void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev);
+
 struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev);
 
 void hinic_free_hwdev(struct hinic_hwdev *hwdev);
 
 int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev);
 
+struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i);
+
+struct hinic_rq *hinic_hwdev_get_rq(struct hinic_hwdev *hwdev, int i);
+
 #endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c
new file mode 100644 (file)
index 0000000..ebe28ee
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include "hinic_hw_if.h"
+#include "hinic_hw_qp.h"
+#include "hinic_hw_io.h"
+
+/**
+ * init_qp - Initialize a Queue Pair
+ * @func_to_io: func to io channel that holds the IO components
+ * @qp: pointer to the qp to initialize
+ * @q_id: the id of the qp
+ * @sq_msix_entry: msix entry for sq
+ * @rq_msix_entry: msix entry for rq
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static int init_qp(struct hinic_func_to_io *func_to_io,
+                  struct hinic_qp *qp, int q_id,
+                  struct msix_entry *sq_msix_entry,
+                  struct msix_entry *rq_msix_entry)
+{
+       /* should be implemented */
+       return 0;
+}
+
+/**
+ * destroy_qp - Clean the resources of a Queue Pair
+ * @func_to_io: func to io channel that holds the IO components
+ * @qp: pointer to the qp to clean
+ **/
+static void destroy_qp(struct hinic_func_to_io *func_to_io,
+                      struct hinic_qp *qp)
+{
+       /* should be implemented */
+}
+
+/**
+ * hinic_io_create_qps - Create Queue Pairs
+ * @func_to_io: func to io channel that holds the IO components
+ * @base_qpn: base qp number
+ * @num_qps: number queue pairs to create
+ * @sq_msix_entry: msix entries for sq
+ * @rq_msix_entry: msix entries for rq
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+int hinic_io_create_qps(struct hinic_func_to_io *func_to_io,
+                       u16 base_qpn, int num_qps,
+                       struct msix_entry *sq_msix_entries,
+                       struct msix_entry *rq_msix_entries)
+{
+       struct hinic_hwif *hwif = func_to_io->hwif;
+       struct pci_dev *pdev = hwif->pdev;
+       size_t qps_size;
+       int i, j, err;
+
+       qps_size = num_qps * sizeof(*func_to_io->qps);
+       func_to_io->qps = devm_kzalloc(&pdev->dev, qps_size, GFP_KERNEL);
+       if (!func_to_io->qps)
+               return -ENOMEM;
+
+       for (i = 0; i < num_qps; i++) {
+               err = init_qp(func_to_io, &func_to_io->qps[i], i,
+                             &sq_msix_entries[i], &rq_msix_entries[i]);
+               if (err) {
+                       dev_err(&pdev->dev, "Failed to create QP %d\n", i);
+                       goto err_init_qp;
+               }
+       }
+
+       return 0;
+
+err_init_qp:
+       for (j = 0; j < i; j++)
+               destroy_qp(func_to_io, &func_to_io->qps[j]);
+
+       devm_kfree(&pdev->dev, func_to_io->qps);
+       return err;
+}
+
+/**
+ * hinic_io_destroy_qps - Destroy the IO Queue Pairs
+ * @func_to_io: func to io channel that holds the IO components
+ * @num_qps: number queue pairs to destroy
+ **/
+void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io, int num_qps)
+{
+       struct hinic_hwif *hwif = func_to_io->hwif;
+       struct pci_dev *pdev = hwif->pdev;
+       int i;
+
+       for (i = 0; i < num_qps; i++)
+               destroy_qp(func_to_io, &func_to_io->qps[i]);
+
+       devm_kfree(&pdev->dev, func_to_io->qps);
+}
+
+/**
+ * hinic_io_init - Initialize the IO components
+ * @func_to_io: func to io channel that holds the IO components
+ * @hwif: HW interface for accessing IO
+ * @max_qps: maximum QPs in HW
+ * @num_ceqs: number completion event queues
+ * @ceq_msix_entries: msix entries for ceqs
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+int hinic_io_init(struct hinic_func_to_io *func_to_io,
+                 struct hinic_hwif *hwif, u16 max_qps, int num_ceqs,
+                 struct msix_entry *ceq_msix_entries)
+{
+       func_to_io->hwif = hwif;
+       func_to_io->qps = NULL;
+       func_to_io->max_qps = max_qps;
+
+       return 0;
+}
+
+/**
+ * hinic_io_free - Free the IO components
+ * @func_to_io: func to io channel that holds the IO components
+ **/
+void hinic_io_free(struct hinic_func_to_io *func_to_io)
+{
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h
new file mode 100644 (file)
index 0000000..7cdcffd
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#ifndef HINIC_HW_IO_H
+#define HINIC_HW_IO_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#include "hinic_hw_if.h"
+#include "hinic_hw_qp.h"
+
+struct hinic_func_to_io {
+       struct hinic_hwif       *hwif;
+
+       struct hinic_qp         *qps;
+       u16                     max_qps;
+};
+
+int hinic_io_create_qps(struct hinic_func_to_io *func_to_io,
+                       u16 base_qpn, int num_qps,
+                       struct msix_entry *sq_msix_entries,
+                       struct msix_entry *rq_msix_entries);
+
+void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io,
+                         int num_qps);
+
+int hinic_io_init(struct hinic_func_to_io *func_to_io,
+                 struct hinic_hwif *hwif, u16 max_qps, int num_ceqs,
+                 struct msix_entry *ceq_msix_entries);
+
+void hinic_io_free(struct hinic_func_to_io *func_to_io);
+
+#endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h
new file mode 100644 (file)
index 0000000..64330fb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#ifndef HINIC_HW_QP_H
+#define HINIC_HW_QP_H
+
+struct hinic_sq {
+       /* should be implemented */
+};
+
+struct hinic_rq {
+       /* should be implemented */
+};
+
+struct hinic_qp {
+       struct hinic_sq         sq;
+       struct hinic_rq         rq;
+};
+
+#endif
index 7aebc6207931d9ad984817f1bc113617a3a5fdc7..22d5b61b04267717001fc0523e1935da906acbfe 100644 (file)
 #include <linux/delay.h>
 #include <linux/err.h>
 
+#include "hinic_hw_qp.h"
 #include "hinic_hw_dev.h"
 #include "hinic_port.h"
+#include "hinic_tx.h"
+#include "hinic_rx.h"
 #include "hinic_dev.h"
 
 MODULE_AUTHOR("Huawei Technologies CO., Ltd");
@@ -57,17 +60,164 @@ MODULE_LICENSE("GPL");
 
 static int change_mac_addr(struct net_device *netdev, const u8 *addr);
 
+/**
+ * create_txqs - Create the Logical Tx Queues of specific NIC device
+ * @nic_dev: the specific NIC device
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static int create_txqs(struct hinic_dev *nic_dev)
+{
+       int err, i, j, num_txqs = hinic_hwdev_num_qps(nic_dev->hwdev);
+       struct net_device *netdev = nic_dev->netdev;
+       size_t txq_size;
+
+       if (nic_dev->txqs)
+               return -EINVAL;
+
+       txq_size = num_txqs * sizeof(*nic_dev->txqs);
+       nic_dev->txqs = devm_kzalloc(&netdev->dev, txq_size, GFP_KERNEL);
+       if (!nic_dev->txqs)
+               return -ENOMEM;
+
+       for (i = 0; i < num_txqs; i++) {
+               struct hinic_sq *sq = hinic_hwdev_get_sq(nic_dev->hwdev, i);
+
+               err = hinic_init_txq(&nic_dev->txqs[i], sq, netdev);
+               if (err) {
+                       netif_err(nic_dev, drv, netdev,
+                                 "Failed to init Txq\n");
+                       goto err_init_txq;
+               }
+       }
+
+       return 0;
+
+err_init_txq:
+       for (j = 0; j < i; j++)
+               hinic_clean_txq(&nic_dev->txqs[j]);
+
+       devm_kfree(&netdev->dev, nic_dev->txqs);
+       return err;
+}
+
+/**
+ * free_txqs - Free the Logical Tx Queues of specific NIC device
+ * @nic_dev: the specific NIC device
+ **/
+static void free_txqs(struct hinic_dev *nic_dev)
+{
+       int i, num_txqs = hinic_hwdev_num_qps(nic_dev->hwdev);
+       struct net_device *netdev = nic_dev->netdev;
+
+       if (!nic_dev->txqs)
+               return;
+
+       for (i = 0; i < num_txqs; i++)
+               hinic_clean_txq(&nic_dev->txqs[i]);
+
+       devm_kfree(&netdev->dev, nic_dev->txqs);
+       nic_dev->txqs = NULL;
+}
+
+/**
+ * create_txqs - Create the Logical Rx Queues of specific NIC device
+ * @nic_dev: the specific NIC device
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static int create_rxqs(struct hinic_dev *nic_dev)
+{
+       int err, i, j, num_rxqs = hinic_hwdev_num_qps(nic_dev->hwdev);
+       struct net_device *netdev = nic_dev->netdev;
+       size_t rxq_size;
+
+       if (nic_dev->rxqs)
+               return -EINVAL;
+
+       rxq_size = num_rxqs * sizeof(*nic_dev->rxqs);
+       nic_dev->rxqs = devm_kzalloc(&netdev->dev, rxq_size, GFP_KERNEL);
+       if (!nic_dev->rxqs)
+               return -ENOMEM;
+
+       for (i = 0; i < num_rxqs; i++) {
+               struct hinic_rq *rq = hinic_hwdev_get_rq(nic_dev->hwdev, i);
+
+               err = hinic_init_rxq(&nic_dev->rxqs[i], rq, netdev);
+               if (err) {
+                       netif_err(nic_dev, drv, netdev,
+                                 "Failed to init rxq\n");
+                       goto err_init_rxq;
+               }
+       }
+
+       return 0;
+
+err_init_rxq:
+       for (j = 0; j < i; j++)
+               hinic_clean_rxq(&nic_dev->rxqs[j]);
+
+       devm_kfree(&netdev->dev, nic_dev->rxqs);
+       return err;
+}
+
+/**
+ * free_txqs - Free the Logical Rx Queues of specific NIC device
+ * @nic_dev: the specific NIC device
+ **/
+static void free_rxqs(struct hinic_dev *nic_dev)
+{
+       int i, num_rxqs = hinic_hwdev_num_qps(nic_dev->hwdev);
+       struct net_device *netdev = nic_dev->netdev;
+
+       if (!nic_dev->rxqs)
+               return;
+
+       for (i = 0; i < num_rxqs; i++)
+               hinic_clean_rxq(&nic_dev->rxqs[i]);
+
+       devm_kfree(&netdev->dev, nic_dev->rxqs);
+       nic_dev->rxqs = NULL;
+}
+
 static int hinic_open(struct net_device *netdev)
 {
        struct hinic_dev *nic_dev = netdev_priv(netdev);
        enum hinic_port_link_state link_state;
-       int err, ret;
+       int err, ret, num_qps;
+
+       if (!(nic_dev->flags & HINIC_INTF_UP)) {
+               err = hinic_hwdev_ifup(nic_dev->hwdev);
+               if (err) {
+                       netif_err(nic_dev, drv, netdev,
+                                 "Failed - HW interface up\n");
+                       return err;
+               }
+       }
+
+       err = create_txqs(nic_dev);
+       if (err) {
+               netif_err(nic_dev, drv, netdev,
+                         "Failed to create Tx queues\n");
+               goto err_create_txqs;
+       }
+
+       err = create_rxqs(nic_dev);
+       if (err) {
+               netif_err(nic_dev, drv, netdev,
+                         "Failed to create Rx queues\n");
+               goto err_create_rxqs;
+       }
+
+       num_qps = hinic_hwdev_num_qps(nic_dev->hwdev);
+       netif_set_real_num_tx_queues(netdev, num_qps);
+       netif_set_real_num_rx_queues(netdev, num_qps);
 
        err = hinic_port_set_state(nic_dev, HINIC_PORT_ENABLE);
        if (err) {
                netif_err(nic_dev, drv, netdev,
                          "Failed to set port state\n");
-               return err;
+               goto err_port_state;
        }
 
        /* Wait up to 3 sec between port enable to link state */
@@ -104,6 +254,16 @@ err_port_link:
        if (ret)
                netif_warn(nic_dev, drv, netdev,
                           "Failed to revert port state\n");
+
+err_port_state:
+       free_rxqs(nic_dev);
+
+err_create_rxqs:
+       free_txqs(nic_dev);
+
+err_create_txqs:
+       if (!(nic_dev->flags & HINIC_INTF_UP))
+               hinic_hwdev_ifdown(nic_dev->hwdev);
        return err;
 }
 
@@ -130,6 +290,12 @@ static int hinic_close(struct net_device *netdev)
                return err;
        }
 
+       free_rxqs(nic_dev);
+       free_txqs(nic_dev);
+
+       if (flags & HINIC_INTF_UP)
+               hinic_hwdev_ifdown(nic_dev->hwdev);
+
        netif_info(nic_dev, drv, netdev, "HINIC_INTF is DOWN\n");
        return 0;
 }
@@ -496,6 +662,8 @@ static int nic_dev_init(struct pci_dev *pdev)
        nic_dev->hwdev  = hwdev;
        nic_dev->msg_enable = MSG_ENABLE_DEFAULT;
        nic_dev->flags = 0;
+       nic_dev->txqs = NULL;
+       nic_dev->rxqs = NULL;
 
        sema_init(&nic_dev->mgmt_lock, 1);
 
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c
new file mode 100644 (file)
index 0000000..3c79f65
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/u64_stats_sync.h>
+
+#include "hinic_hw_qp.h"
+#include "hinic_rx.h"
+
+/**
+ * hinic_rxq_clean_stats - Clean the statistics of specific queue
+ * @rxq: Logical Rx Queue
+ **/
+void hinic_rxq_clean_stats(struct hinic_rxq *rxq)
+{
+       struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats;
+
+       u64_stats_update_begin(&rxq_stats->syncp);
+       rxq_stats->pkts  = 0;
+       rxq_stats->bytes = 0;
+       u64_stats_update_end(&rxq_stats->syncp);
+}
+
+/**
+ * rxq_stats_init - Initialize the statistics of specific queue
+ * @rxq: Logical Rx Queue
+ **/
+static void rxq_stats_init(struct hinic_rxq *rxq)
+{
+       struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats;
+
+       u64_stats_init(&rxq_stats->syncp);
+       hinic_rxq_clean_stats(rxq);
+}
+
+/**
+ * hinic_init_rxq - Initialize the Rx Queue
+ * @rxq: Logical Rx Queue
+ * @rq: Hardware Rx Queue to connect the Logical queue with
+ * @netdev: network device to connect the Logical queue with
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq,
+                  struct net_device *netdev)
+{
+       rxq->netdev = netdev;
+       rxq->rq = rq;
+
+       rxq_stats_init(rxq);
+       return 0;
+}
+
+/**
+ * hinic_clean_rxq - Clean the Rx Queue
+ * @rxq: Logical Rx Queue
+ **/
+void hinic_clean_rxq(struct hinic_rxq *rxq)
+{
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.h b/drivers/net/ethernet/huawei/hinic/hinic_rx.h
new file mode 100644 (file)
index 0000000..fbd0246
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#ifndef HINIC_RX_H
+#define HINIC_RX_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/u64_stats_sync.h>
+
+#include "hinic_hw_qp.h"
+
+struct hinic_rxq_stats {
+       u64                     pkts;
+       u64                     bytes;
+
+       struct u64_stats_sync   syncp;
+};
+
+struct hinic_rxq {
+       struct net_device       *netdev;
+       struct hinic_rq         *rq;
+
+       struct hinic_rxq_stats  rxq_stats;
+};
+
+void hinic_rxq_clean_stats(struct hinic_rxq *rxq);
+
+int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq,
+                  struct net_device *netdev);
+
+void hinic_clean_rxq(struct hinic_rxq *rxq);
+
+#endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c
new file mode 100644 (file)
index 0000000..9835912
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/u64_stats_sync.h>
+
+#include "hinic_hw_qp.h"
+#include "hinic_tx.h"
+
+/**
+ * hinic_txq_clean_stats - Clean the statistics of specific queue
+ * @txq: Logical Tx Queue
+ **/
+void hinic_txq_clean_stats(struct hinic_txq *txq)
+{
+       struct hinic_txq_stats *txq_stats = &txq->txq_stats;
+
+       u64_stats_update_begin(&txq_stats->syncp);
+       txq_stats->pkts    = 0;
+       txq_stats->bytes   = 0;
+       txq_stats->tx_busy = 0;
+       txq_stats->tx_wake = 0;
+       txq_stats->tx_dropped = 0;
+       u64_stats_update_end(&txq_stats->syncp);
+}
+
+/**
+ * txq_stats_init - Initialize the statistics of specific queue
+ * @txq: Logical Tx Queue
+ **/
+static void txq_stats_init(struct hinic_txq *txq)
+{
+       struct hinic_txq_stats *txq_stats = &txq->txq_stats;
+
+       u64_stats_init(&txq_stats->syncp);
+       hinic_txq_clean_stats(txq);
+}
+
+/**
+ * hinic_init_txq - Initialize the Tx Queue
+ * @txq: Logical Tx Queue
+ * @sq: Hardware Tx Queue to connect the Logical queue with
+ * @netdev: network device to connect the Logical queue with
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq,
+                  struct net_device *netdev)
+{
+       txq->netdev = netdev;
+       txq->sq = sq;
+
+       txq_stats_init(txq);
+       return 0;
+}
+
+/**
+ * hinic_clean_txq - Clean the Tx Queue
+ * @txq: Logical Tx Queue
+ **/
+void hinic_clean_txq(struct hinic_txq *txq)
+{
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h
new file mode 100644 (file)
index 0000000..bbdb4b6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#ifndef HINIC_TX_H
+#define HINIC_TX_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/u64_stats_sync.h>
+
+#include "hinic_hw_qp.h"
+
+struct hinic_txq_stats {
+       u64     pkts;
+       u64     bytes;
+       u64     tx_busy;
+       u64     tx_wake;
+       u64     tx_dropped;
+
+       struct u64_stats_sync   syncp;
+};
+
+struct hinic_txq {
+       struct net_device       *netdev;
+       struct hinic_sq         *sq;
+
+       struct hinic_txq_stats  txq_stats;
+};
+
+void hinic_txq_clean_stats(struct hinic_txq *txq);
+
+int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq,
+                  struct net_device *netdev);
+
+void hinic_clean_txq(struct hinic_txq *txq);
+
+#endif