net: hns3: Add mailbox support to PF driver
authorSalil Mehta <salil.mehta@huawei.com>
Thu, 14 Dec 2017 18:03:07 +0000 (18:03 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 15 Dec 2017 15:55:35 +0000 (10:55 -0500)
Command queue provides the provision of Mailbox command which
can be used for communication between PF and VF. PF handles
messages from various VFs for fetching various information like,
queue, vlan, link status related etc. It also handles the request
from various VFs to perform certain privileged operations.

This patch adds the support of a message handler for handling
such various command requests from VF.

Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: lipeng <lipeng321@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c [new file with mode: 0644]

index d077fa02b66e363fb8c4d66f2b1e6d33a91ac8f2..cb8ddd04347686ac436b1f3b8acfc155cf4db685 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
 #
 # Makefile for the HISILICON network device drivers.
 #
@@ -5,6 +6,6 @@
 ccflags-y := -Idrivers/net/ethernet/hisilicon/hns3
 
 obj-$(CONFIG_HNS3_HCLGE) += hclge.o
-hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o
+hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o hclge_mbx.o
 
 hclge-$(CONFIG_HNS3_DCB) += hclge_dcb.o
index d07c700c7ff8717ca78d2292378d74d568324675..980fcdf688080a7fd3970032247a147db8441782 100644 (file)
@@ -21,6 +21,7 @@
 #include "hclge_cmd.h"
 #include "hclge_dcb.h"
 #include "hclge_main.h"
+#include "hclge_mbx.h"
 #include "hclge_mdio.h"
 #include "hclge_tm.h"
 #include "hnae3.h"
index aacec438b93320cd3e49840621bae47f81717616..028817c7d67c0dd5d9a60cc933b4bd0ab0fe5b42 100644 (file)
@@ -554,4 +554,6 @@ int hclge_set_vf_vlan_common(struct hclge_dev *vport, int vfid,
 
 int hclge_buffer_alloc(struct hclge_dev *hdev);
 int hclge_rss_init_hw(struct hclge_dev *hdev);
+
+void hclge_mbx_handler(struct hclge_dev *hdev);
 #endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
new file mode 100644 (file)
index 0000000..24b1f14
--- /dev/null
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2016-2017 Hisilicon Limited.
+
+#include "hclge_main.h"
+#include "hclge_mbx.h"
+#include "hnae3.h"
+
+/* hclge_gen_resp_to_vf: used to generate a synchronous response to VF when PF
+ * receives a mailbox message from VF.
+ * @vport: pointer to struct hclge_vport
+ * @vf_to_pf_req: pointer to hclge_mbx_vf_to_pf_cmd of the original mailbox
+ *               message
+ * @resp_status: indicate to VF whether its request success(0) or failed.
+ */
+static int hclge_gen_resp_to_vf(struct hclge_vport *vport,
+                               struct hclge_mbx_vf_to_pf_cmd *vf_to_pf_req,
+                               int resp_status,
+                               u8 *resp_data, u16 resp_data_len)
+{
+       struct hclge_mbx_pf_to_vf_cmd *resp_pf_to_vf;
+       struct hclge_dev *hdev = vport->back;
+       enum hclge_cmd_status status;
+       struct hclge_desc desc;
+
+       resp_pf_to_vf = (struct hclge_mbx_pf_to_vf_cmd *)desc.data;
+
+       if (resp_data_len > HCLGE_MBX_MAX_RESP_DATA_SIZE) {
+               dev_err(&hdev->pdev->dev,
+                       "PF fail to gen resp to VF len %d exceeds max len %d\n",
+                       resp_data_len,
+                       HCLGE_MBX_MAX_RESP_DATA_SIZE);
+       }
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGEVF_OPC_MBX_PF_TO_VF, false);
+
+       resp_pf_to_vf->dest_vfid = vf_to_pf_req->mbx_src_vfid;
+       resp_pf_to_vf->msg_len = vf_to_pf_req->msg_len;
+
+       resp_pf_to_vf->msg[0] = HCLGE_MBX_PF_VF_RESP;
+       resp_pf_to_vf->msg[1] = vf_to_pf_req->msg[0];
+       resp_pf_to_vf->msg[2] = vf_to_pf_req->msg[1];
+       resp_pf_to_vf->msg[3] = (resp_status == 0) ? 0 : 1;
+
+       if (resp_data && resp_data_len > 0)
+               memcpy(&resp_pf_to_vf->msg[4], resp_data, resp_data_len);
+
+       status = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (status)
+               dev_err(&hdev->pdev->dev,
+                       "PF failed(=%d) to send response to VF\n", status);
+
+       return status;
+}
+
+static int hclge_send_mbx_msg(struct hclge_vport *vport, u8 *msg, u16 msg_len,
+                             u16 mbx_opcode, u8 dest_vfid)
+{
+       struct hclge_mbx_pf_to_vf_cmd *resp_pf_to_vf;
+       struct hclge_dev *hdev = vport->back;
+       enum hclge_cmd_status status;
+       struct hclge_desc desc;
+
+       resp_pf_to_vf = (struct hclge_mbx_pf_to_vf_cmd *)desc.data;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGEVF_OPC_MBX_PF_TO_VF, false);
+
+       resp_pf_to_vf->dest_vfid = dest_vfid;
+       resp_pf_to_vf->msg_len = msg_len;
+       resp_pf_to_vf->msg[0] = mbx_opcode;
+
+       memcpy(&resp_pf_to_vf->msg[1], msg, msg_len);
+
+       status = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (status)
+               dev_err(&hdev->pdev->dev,
+                       "PF failed(=%d) to send mailbox message to VF\n",
+                       status);
+
+       return status;
+}
+
+static int hclge_set_vf_promisc_mode(struct hclge_vport *vport,
+                                    struct hclge_mbx_vf_to_pf_cmd *req)
+{
+       bool en = req->msg[1] ? true : false;
+       struct hclge_promisc_param param;
+
+       /* always enable broadcast promisc bit */
+       hclge_promisc_param_init(&param, en, en, true, vport->vport_id);
+       return hclge_cmd_set_promisc_mode(vport->back, &param);
+}
+
+static int hclge_set_vf_uc_mac_addr(struct hclge_vport *vport,
+                                   struct hclge_mbx_vf_to_pf_cmd *mbx_req,
+                                   bool gen_resp)
+{
+       const u8 *mac_addr = (const u8 *)(&mbx_req->msg[2]);
+       struct hclge_dev *hdev = vport->back;
+       int status;
+
+       if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_UC_MODIFY) {
+               const u8 *old_addr = (const u8 *)(&mbx_req->msg[8]);
+
+               hclge_rm_uc_addr_common(vport, old_addr);
+               status = hclge_add_uc_addr_common(vport, mac_addr);
+       } else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_UC_ADD) {
+               status = hclge_add_uc_addr_common(vport, mac_addr);
+       } else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_UC_REMOVE) {
+               status = hclge_rm_uc_addr_common(vport, mac_addr);
+       } else {
+               dev_err(&hdev->pdev->dev,
+                       "failed to set unicast mac addr, unknown subcode %d\n",
+                       mbx_req->msg[1]);
+               return -EIO;
+       }
+
+       if (gen_resp)
+               hclge_gen_resp_to_vf(vport, mbx_req, status, NULL, 0);
+
+       return 0;
+}
+
+static int hclge_set_vf_mc_mac_addr(struct hclge_vport *vport,
+                                   struct hclge_mbx_vf_to_pf_cmd *mbx_req,
+                                   bool gen_resp)
+{
+       const u8 *mac_addr = (const u8 *)(&mbx_req->msg[2]);
+       struct hclge_dev *hdev = vport->back;
+       int status;
+
+       if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_MC_ADD) {
+               status = hclge_add_mc_addr_common(vport, mac_addr);
+       } else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_MC_REMOVE) {
+               status = hclge_rm_mc_addr_common(vport, mac_addr);
+       } else if (mbx_req->msg[1] == HCLGE_MBX_MAC_VLAN_MC_FUNC_MTA_ENABLE) {
+               u8 func_id = vport->vport_id;
+               bool enable = mbx_req->msg[2];
+
+               status = hclge_cfg_func_mta_filter(hdev, func_id, enable);
+       } else {
+               dev_err(&hdev->pdev->dev,
+                       "failed to set mcast mac addr, unknown subcode %d\n",
+                       mbx_req->msg[1]);
+               return -EIO;
+       }
+
+       if (gen_resp)
+               hclge_gen_resp_to_vf(vport, mbx_req, status, NULL, 0);
+
+       return 0;
+}
+
+static int hclge_set_vf_vlan_cfg(struct hclge_vport *vport,
+                                struct hclge_mbx_vf_to_pf_cmd *mbx_req,
+                                bool gen_resp)
+{
+       struct hclge_dev *hdev = vport->back;
+       int status = 0;
+
+       if (mbx_req->msg[1] == HCLGE_MBX_VLAN_FILTER) {
+               u16 vlan, proto;
+               bool is_kill;
+
+               is_kill = !!mbx_req->msg[2];
+               memcpy(&vlan, &mbx_req->msg[3], sizeof(vlan));
+               memcpy(&proto, &mbx_req->msg[5], sizeof(proto));
+               status = hclge_set_vf_vlan_common(hdev, vport->vport_id,
+                                                 is_kill, vlan, 0,
+                                                 cpu_to_be16(proto));
+       }
+
+       if (gen_resp)
+               status = hclge_gen_resp_to_vf(vport, mbx_req, status, NULL, 0);
+
+       return status;
+}
+
+static int hclge_get_vf_tcinfo(struct hclge_vport *vport,
+                              struct hclge_mbx_vf_to_pf_cmd *mbx_req,
+                              bool gen_resp)
+{
+       struct hclge_dev *hdev = vport->back;
+       int ret;
+
+       ret = hclge_gen_resp_to_vf(vport, mbx_req, 0, &hdev->hw_tc_map,
+                                  sizeof(u8));
+
+       return ret;
+}
+
+static int hclge_get_vf_queue_info(struct hclge_vport *vport,
+                                  struct hclge_mbx_vf_to_pf_cmd *mbx_req,
+                                  bool gen_resp)
+{
+#define HCLGE_TQPS_RSS_INFO_LEN                8
+       u8 resp_data[HCLGE_TQPS_RSS_INFO_LEN];
+       struct hclge_dev *hdev = vport->back;
+
+       /* get the queue related info */
+       memcpy(&resp_data[0], &vport->alloc_tqps, sizeof(u16));
+       memcpy(&resp_data[2], &hdev->rss_size_max, sizeof(u16));
+       memcpy(&resp_data[4], &hdev->num_desc, sizeof(u16));
+       memcpy(&resp_data[6], &hdev->rx_buf_len, sizeof(u16));
+
+       return hclge_gen_resp_to_vf(vport, mbx_req, 0, resp_data,
+                                   HCLGE_TQPS_RSS_INFO_LEN);
+}
+
+static int hclge_get_link_info(struct hclge_vport *vport,
+                              struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+       struct hclge_dev *hdev = vport->back;
+       u16 link_status;
+       u8 msg_data[2];
+       u8 dest_vfid;
+
+       /* mac.link can only be 0 or 1 */
+       link_status = (u16)hdev->hw.mac.link;
+       memcpy(&msg_data[0], &link_status, sizeof(u16));
+       dest_vfid = mbx_req->mbx_src_vfid;
+
+       /* send this requested info to VF */
+       return hclge_send_mbx_msg(vport, msg_data, sizeof(u8),
+                                 HCLGE_MBX_LINK_STAT_CHANGE, dest_vfid);
+}
+
+void hclge_mbx_handler(struct hclge_dev *hdev)
+{
+       struct hclge_cmq_ring *crq = &hdev->hw.cmq.crq;
+       struct hclge_mbx_vf_to_pf_cmd *req;
+       struct hclge_vport *vport;
+       struct hclge_desc *desc;
+       int ret;
+
+       /* handle all the mailbox requests in the queue */
+       while (hnae_get_bit(crq->desc[crq->next_to_use].flag,
+                           HCLGE_CMDQ_RX_OUTVLD_B)) {
+               desc = &crq->desc[crq->next_to_use];
+               req = (struct hclge_mbx_vf_to_pf_cmd *)desc->data;
+
+               vport = &hdev->vport[req->mbx_src_vfid];
+
+               switch (req->msg[0]) {
+               case HCLGE_MBX_SET_PROMISC_MODE:
+                       ret = hclge_set_vf_promisc_mode(vport, req);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF fail(%d) to set VF promisc mode\n",
+                                       ret);
+                       break;
+               case HCLGE_MBX_SET_UNICAST:
+                       ret = hclge_set_vf_uc_mac_addr(vport, req, false);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF fail(%d) to set VF UC MAC Addr\n",
+                                       ret);
+                       break;
+               case HCLGE_MBX_SET_MULTICAST:
+                       ret = hclge_set_vf_mc_mac_addr(vport, req, false);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF fail(%d) to set VF MC MAC Addr\n",
+                                       ret);
+                       break;
+               case HCLGE_MBX_SET_VLAN:
+                       ret = hclge_set_vf_vlan_cfg(vport, req, false);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF failed(%d) to config VF's VLAN\n",
+                                       ret);
+                       break;
+               case HCLGE_MBX_GET_QINFO:
+                       ret = hclge_get_vf_queue_info(vport, req, true);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF failed(%d) to get Q info for VF\n",
+                                       ret);
+                       break;
+               case HCLGE_MBX_GET_TCINFO:
+                       ret = hclge_get_vf_tcinfo(vport, req, true);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF failed(%d) to get TC info for VF\n",
+                                       ret);
+                       break;
+               case HCLGE_MBX_GET_LINK_STATUS:
+                       ret = hclge_get_link_info(vport, req);
+                       if (ret)
+                               dev_err(&hdev->pdev->dev,
+                                       "PF fail(%d) to get link stat for VF\n",
+                                       ret);
+                       break;
+               default:
+                       dev_err(&hdev->pdev->dev,
+                               "un-supported mailbox message, code = %d\n",
+                               req->msg[0]);
+                       break;
+               }
+               hclge_mbx_ring_ptr_move_crq(crq);
+       }
+
+       /* Write back CMDQ_RQ header pointer, M7 need this pointer */
+       hclge_write_dev(&hdev->hw, HCLGE_NIC_CRQ_HEAD_REG, crq->next_to_use);
+}