Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  hinic_hw_dev.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Huawei HiNIC PCI Express Linux driver
 * Copyright(c) 2017 Huawei Technologies Co., Ltd
 */


#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/log2.h>
#include <linux/err.h>
#include <linux/netdevice.h>
#include <net/devlink.h>

#include "hinic_devlink.h"
#include "hinic_sriov.h"
#include "hinic_dev.h"
#include "hinic_hw_if.h"
#include "hinic_hw_eqs.h"
#include "hinic_hw_mgmt.h"
#include "hinic_hw_qp_ctxt.h"
#include "hinic_hw_qp.h"
#include "hinic_hw_io.h"
#include "hinic_hw_dev.h"

#define OUTBOUND_STATE_TIMEOUT          100
#define DB_STATE_TIMEOUT                100

#define MAX_IRQS(max_qps, num_aeqs, num_ceqs)   \
   (2 * (max_qps) + (num_aeqs) + (num_ceqs))

#define ADDR_IN_4BYTES(addr)            ((addr) >> 2)

enum intr_type {
 INTR_MSIX_TYPE,
};

/**
 * parse_capability - convert device capabilities to NIC capabilities
 * @hwdev: the HW device to set and convert device capabilities for
 * @dev_cap: device capabilities from FW
 *
 * Return 0 - Success, negative - Failure
 **/

static int parse_capability(struct hinic_hwdev *hwdev,
       struct hinic_dev_cap *dev_cap)
{
 struct hinic_cap *nic_cap = &hwdev->nic_cap;
 int num_aeqs, num_ceqs, num_irqs;

 if (!HINIC_IS_VF(hwdev->hwif) && dev_cap->intr_type != INTR_MSIX_TYPE)
  return -EFAULT;

 num_aeqs = HINIC_HWIF_NUM_AEQS(hwdev->hwif);
 num_ceqs = HINIC_HWIF_NUM_CEQS(hwdev->hwif);
 num_irqs = HINIC_HWIF_NUM_IRQS(hwdev->hwif);

 /* Each QP has its own (SQ + RQ) interrupts */
 nic_cap->num_qps = (num_irqs - (num_aeqs + num_ceqs)) / 2;

 if (nic_cap->num_qps > HINIC_Q_CTXT_MAX)
  nic_cap->num_qps = HINIC_Q_CTXT_MAX;

 if (!HINIC_IS_VF(hwdev->hwif))
  nic_cap->max_qps = dev_cap->max_sqs + 1;
 else
  nic_cap->max_qps = dev_cap->max_sqs;

 if (nic_cap->num_qps > nic_cap->max_qps)
  nic_cap->num_qps = nic_cap->max_qps;

 if (!HINIC_IS_VF(hwdev->hwif)) {
  nic_cap->max_vf = dev_cap->max_vf;
  nic_cap->max_vf_qps = dev_cap->max_vf_sqs + 1;
 }

 hwdev->port_id = dev_cap->port_id;

 return 0;
}

/**
 * get_capability - get device capabilities from FW
 * @pfhwdev: the PF HW device to get capabilities for
 *
 * Return 0 - Success, negative - Failure
 **/

static int get_capability(struct hinic_pfhwdev *pfhwdev)
{
 struct hinic_hwdev *hwdev = &pfhwdev->hwdev;
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;
 struct hinic_dev_cap dev_cap;
 u16 out_len;
 int err;

 out_len = sizeof(dev_cap);

 err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_CFGM,
    HINIC_CFG_NIC_CAP, &dev_cap, sizeof(dev_cap),
    &dev_cap, &out_len, HINIC_MGMT_MSG_SYNC);
 if (err) {
  dev_err(&pdev->dev, "Failed to get capability from FW\n");
  return err;
 }

 return parse_capability(hwdev, &dev_cap);
}

/**
 * get_dev_cap - get device capabilities
 * @hwdev: the NIC HW device to get capabilities for
 *
 * Return 0 - Success, negative - Failure
 **/

static int get_dev_cap(struct hinic_hwdev *hwdev)
{
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;
 struct hinic_pfhwdev *pfhwdev;
 int err;

 switch (HINIC_FUNC_TYPE(hwif)) {
 case HINIC_PPF:
 case HINIC_PF:
 case HINIC_VF:
  pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
  err = get_capability(pfhwdev);
  if (err) {
   dev_err(&pdev->dev, "Failed to get capability\n");
   return err;
  }
  break;
 default:
  dev_err(&pdev->dev, "Unsupported PCI Function type\n");
  return -EINVAL;
 }

 return 0;
}

/**
 * init_msix - enable the msix and save the entries
 * @hwdev: the NIC HW device
 *
 * Return 0 - Success, negative - Failure
 **/

static int init_msix(struct hinic_hwdev *hwdev)
{
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;
 int nr_irqs, num_aeqs, num_ceqs;
 int i, err;

 num_aeqs = HINIC_HWIF_NUM_AEQS(hwif);
 num_ceqs = HINIC_HWIF_NUM_CEQS(hwif);
 nr_irqs = MAX_IRQS(HINIC_MAX_QPS, num_aeqs, num_ceqs);
 if (nr_irqs > HINIC_HWIF_NUM_IRQS(hwif))
  nr_irqs = HINIC_HWIF_NUM_IRQS(hwif);

 hwdev->msix_entries = devm_kcalloc(&pdev->dev, nr_irqs,
        sizeof(*hwdev->msix_entries),
        GFP_KERNEL);
 if (!hwdev->msix_entries)
  return -ENOMEM;

 for (i = 0; i < nr_irqs; i++)
  hwdev->msix_entries[i].entry = i;

 err = pci_enable_msix_exact(pdev, hwdev->msix_entries, nr_irqs);
 if (err) {
  dev_err(&pdev->dev, "Failed to enable pci msix\n");
  return err;
 }

 return 0;
}

/**
 * disable_msix - disable the msix
 * @hwdev: the NIC HW device
 **/

static void disable_msix(struct hinic_hwdev *hwdev)
{
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;

 pci_disable_msix(pdev);
}

/**
 * hinic_port_msg_cmd - send port msg to mgmt
 * @hwdev: the NIC HW device
 * @cmd: the port command
 * @buf_in: input buffer
 * @in_size: input size
 * @buf_out: output buffer
 * @out_size: returned output size
 *
 * Return 0 - Success, negative - Failure
 **/

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)
{
 struct hinic_pfhwdev *pfhwdev;

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

 return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, cmd,
     buf_in, in_size, buf_out, out_size,
     HINIC_MGMT_MSG_SYNC);
}

int hinic_hilink_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_hilink_cmd cmd,
    void *buf_in, u16 in_size, void *buf_out,
    u16 *out_size)
{
 struct hinic_pfhwdev *pfhwdev;

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

 return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_HILINK, cmd,
     buf_in, in_size, buf_out, out_size,
     HINIC_MGMT_MSG_SYNC);
}

/**
 * init_fw_ctxt- Init Firmware tables before network mgmt and io operations
 * @hwdev: the NIC HW device
 *
 * Return 0 - Success, negative - Failure
 **/

static int init_fw_ctxt(struct hinic_hwdev *hwdev)
{
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;
 struct hinic_cmd_fw_ctxt fw_ctxt;
 u16 out_size = sizeof(fw_ctxt);
 int err;

 fw_ctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
 fw_ctxt.rx_buf_sz = HINIC_RX_BUF_SZ;

 err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_FWCTXT_INIT,
     &fw_ctxt, sizeof(fw_ctxt),
     &fw_ctxt, &out_size);
 if (err || out_size != sizeof(fw_ctxt) || fw_ctxt.status) {
  dev_err(&pdev->dev, "Failed to init FW ctxt, err: %d, status: 0x%x, out size: 0x%x\n",
   err, fw_ctxt.status, out_size);
  return -EIO;
 }

 return 0;
}

/**
 * set_hw_ioctxt - set the shape of the IO queues in FW
 * @hwdev: the NIC HW device
 * @rq_depth: rq depth
 * @sq_depth: sq depth
 *
 * Return 0 - Success, negative - Failure
 **/

static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int sq_depth,
    unsigned int rq_depth)
{
 struct hinic_hwif *hwif = hwdev->hwif;
 struct hinic_cmd_hw_ioctxt hw_ioctxt;
 struct hinic_pfhwdev *pfhwdev;

 hw_ioctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
 hw_ioctxt.ppf_idx = HINIC_HWIF_PPF_IDX(hwif);

 hw_ioctxt.set_cmdq_depth = HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT;
 hw_ioctxt.cmdq_depth = 0;

 hw_ioctxt.lro_en = 1;

 hw_ioctxt.rq_depth  = ilog2(rq_depth);

 hw_ioctxt.rx_buf_sz_idx = HINIC_RX_BUF_SZ_IDX;

 hw_ioctxt.sq_depth  = ilog2(sq_depth);

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

 return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
     HINIC_COMM_CMD_HWCTXT_SET,
     &hw_ioctxt, sizeof(hw_ioctxt), NULL,
     NULL, HINIC_MGMT_MSG_SYNC);
}

static int wait_for_outbound_state(struct hinic_hwdev *hwdev)
{
 enum hinic_outbound_state outbound_state;
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;
 unsigned long end;

 end = jiffies + msecs_to_jiffies(OUTBOUND_STATE_TIMEOUT);
 do {
  outbound_state = hinic_outbound_state_get(hwif);

  if (outbound_state == HINIC_OUTBOUND_ENABLE)
   return 0;

  msleep(20);
 } while (time_before(jiffies, end));

 dev_err(&pdev->dev, "Wait for OUTBOUND - Timeout\n");
 return -EFAULT;
}

static int wait_for_db_state(struct hinic_hwdev *hwdev)
{
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;
 enum hinic_db_state db_state;
 unsigned long end;

 end = jiffies + msecs_to_jiffies(DB_STATE_TIMEOUT);
 do {
  db_state = hinic_db_state_get(hwif);

  if (db_state == HINIC_DB_ENABLE)
   return 0;

  msleep(20);
 } while (time_before(jiffies, end));

 dev_err(&pdev->dev, "Wait for DB - Timeout\n");
 return -EFAULT;
}

/**
 * clear_io_resources - set the IO resources as not active in the NIC
 * @hwdev: the NIC HW device
 *
 * Return 0 - Success, negative - Failure
 **/

static int clear_io_resources(struct hinic_hwdev *hwdev)
{
 struct hinic_cmd_clear_io_res cmd_clear_io_res;
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;
 struct hinic_pfhwdev *pfhwdev;
 int err;

 /* sleep 100ms to wait for firmware stopping I/O */
 msleep(100);

 cmd_clear_io_res.func_idx = HINIC_HWIF_FUNC_IDX(hwif);

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

 err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
    HINIC_COMM_CMD_IO_RES_CLEAR, &cmd_clear_io_res,
    sizeof(cmd_clear_io_res), NULL, NULL,
    HINIC_MGMT_MSG_SYNC);
 if (err) {
  dev_err(&pdev->dev, "Failed to clear IO resources\n");
  return err;
 }

 return 0;
}

/**
 * set_resources_state - set the state of the resources in the NIC
 * @hwdev: the NIC HW device
 * @state: the state to set
 *
 * Return 0 - Success, negative - Failure
 **/

static int set_resources_state(struct hinic_hwdev *hwdev,
          enum hinic_res_state state)
{
 struct hinic_cmd_set_res_state res_state;
 struct hinic_hwif *hwif = hwdev->hwif;
 struct hinic_pfhwdev *pfhwdev;

 res_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
 res_state.state = state;

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

 return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt,
     HINIC_MOD_COMM,
     HINIC_COMM_CMD_RES_STATE_SET,
     &res_state, sizeof(res_state), NULL,
     NULL, 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;
 u16 out_size = sizeof(cmd_base_qpn);
 struct pci_dev *pdev = hwif->pdev;
 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, err: %d, status: 0x%x, out size: 0x%x\n",
   err, cmd_base_qpn.status, out_size);
  return -EIO;
 }

 *base_qpn = cmd_base_qpn.qpn;
 return 0;
}

/**
 * hinic_hwdev_ifup - Preparing the HW for passing IO
 * @hwdev: the NIC HW device
 * @sq_depth: the send queue depth
 * @rq_depth: the receive queue depth
 *
 * Return 0 - Success, negative - Failure
 **/

int hinic_hwdev_ifup(struct hinic_hwdev *hwdev, u16 sq_depth, u16 rq_depth)
{
 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 *ceq_msix_entries;
 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);

 ceq_msix_entries = &hwdev->msix_entries[num_aeqs];
 func_to_io->hwdev = hwdev;
 func_to_io->sq_depth = sq_depth;
 func_to_io->rq_depth = rq_depth;
 func_to_io->global_qpn = base_qpn;

 err = hinic_io_init(func_to_io, hwif, nic_cap->max_qps, num_ceqs,
       ceq_msix_entries);
 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;
 }

 err = wait_for_db_state(hwdev);
 if (err) {
  dev_warn(&pdev->dev, "db - disabled, try again\n");
  hinic_db_state_set(hwif, HINIC_DB_ENABLE);
 }

 err = set_hw_ioctxt(hwdev, sq_depth, rq_depth);
 if (err) {
  dev_err(&pdev->dev, "Failed to set HW IO ctxt\n");
  goto err_hw_ioctxt;
 }

 return 0;

err_hw_ioctxt:
 hinic_io_destroy_qps(func_to_io, num_qps);

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;

 clear_io_resources(hwdev);

 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
 * @cmd: the mgmt event
 * @handle: private data for the handler
 * @handler: event handler
 **/

void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev,
        enum hinic_mgmt_msg_cmd cmd, void *handle,
        void (*handler)(void *handle, void *buf_in,
          u16 in_size, void *buf_out,
          u16 *out_size))
{
 struct hinic_pfhwdev *pfhwdev;
 struct hinic_nic_cb *nic_cb;
 u8 cmd_cb;

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

 cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE;
 nic_cb = &pfhwdev->nic_cb[cmd_cb];

 nic_cb->handler = handler;
 nic_cb->handle = handle;
 nic_cb->cb_state = HINIC_CB_ENABLED;
}

/**
 * hinic_hwdev_cb_unregister - unregister callback handler for MGMT events
 * @hwdev: the NIC HW device
 * @cmd: the mgmt event
 **/

void hinic_hwdev_cb_unregister(struct hinic_hwdev *hwdev,
          enum hinic_mgmt_msg_cmd cmd)
{
 struct hinic_hwif *hwif = hwdev->hwif;
 struct hinic_pfhwdev *pfhwdev;
 struct hinic_nic_cb *nic_cb;
 u8 cmd_cb;

 if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif))
  return;

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

 cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE;
 nic_cb = &pfhwdev->nic_cb[cmd_cb];

 nic_cb->cb_state &= ~HINIC_CB_ENABLED;

 while (nic_cb->cb_state & HINIC_CB_RUNNING)
  schedule();

 nic_cb->handler = NULL;
}

/**
 * nic_mgmt_msg_handler - nic mgmt event handler
 * @handle: private data for the handler
 * @cmd: message command
 * @buf_in: input buffer
 * @in_size: input size
 * @buf_out: output buffer
 * @out_size: returned output size
 **/

static void nic_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in,
     u16 in_size, void *buf_out, u16 *out_size)
{
 struct hinic_pfhwdev *pfhwdev = handle;
 enum hinic_cb_state cb_state;
 struct hinic_nic_cb *nic_cb;
 struct hinic_hwdev *hwdev;
 struct hinic_hwif *hwif;
 struct pci_dev *pdev;
 u8 cmd_cb;

 hwdev = &pfhwdev->hwdev;
 hwif = hwdev->hwif;
 pdev = hwif->pdev;

 if (cmd < HINIC_MGMT_MSG_CMD_BASE ||
     cmd >= HINIC_MGMT_MSG_CMD_MAX) {
  dev_err(&pdev->dev, "unknown L2NIC event, cmd = %d\n", cmd);
  return;
 }

 cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE;

 nic_cb = &pfhwdev->nic_cb[cmd_cb];

 cb_state = cmpxchg(&nic_cb->cb_state,
      HINIC_CB_ENABLED,
      HINIC_CB_ENABLED | HINIC_CB_RUNNING);

 if (cb_state == HINIC_CB_ENABLED && nic_cb->handler)
  nic_cb->handler(nic_cb->handle, buf_in,
    in_size, buf_out, out_size);
 else
  dev_err(&pdev->dev, "Unhandled NIC Event %d\n", cmd);

 nic_cb->cb_state &= ~HINIC_CB_RUNNING;
}

static void hinic_comm_recv_mgmt_self_cmd_reg(struct hinic_pfhwdev *pfhwdev,
           u8 cmd,
           comm_mgmt_self_msg_proc proc)
{
 u8 cmd_idx;

 cmd_idx = pfhwdev->proc.cmd_num;
 if (cmd_idx >= HINIC_COMM_SELF_CMD_MAX) {
  dev_err(&pfhwdev->hwdev.hwif->pdev->dev,
   "Register recv mgmt process failed, cmd: 0x%x\n", cmd);
  return;
 }

 pfhwdev->proc.info[cmd_idx].cmd = cmd;
 pfhwdev->proc.info[cmd_idx].proc = proc;
 pfhwdev->proc.cmd_num++;
}

static void hinic_comm_recv_mgmt_self_cmd_unreg(struct hinic_pfhwdev *pfhwdev,
      u8 cmd)
{
 u8 cmd_idx;

 cmd_idx = pfhwdev->proc.cmd_num;
 if (cmd_idx >= HINIC_COMM_SELF_CMD_MAX) {
  dev_err(&pfhwdev->hwdev.hwif->pdev->dev, "Unregister recv mgmt process failed, cmd: 0x%x\n",
   cmd);
  return;
 }

 for (cmd_idx = 0; cmd_idx < HINIC_COMM_SELF_CMD_MAX; cmd_idx++) {
  if (cmd == pfhwdev->proc.info[cmd_idx].cmd) {
   pfhwdev->proc.info[cmd_idx].cmd = 0;
   pfhwdev->proc.info[cmd_idx].proc = NULL;
   pfhwdev->proc.cmd_num--;
  }
 }
}

static void comm_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in,
      u16 in_size, void *buf_out, u16 *out_size)
{
 struct hinic_pfhwdev *pfhwdev = handle;
 u8 cmd_idx;

 for (cmd_idx = 0; cmd_idx < pfhwdev->proc.cmd_num; cmd_idx++) {
  if (cmd == pfhwdev->proc.info[cmd_idx].cmd) {
   if (!pfhwdev->proc.info[cmd_idx].proc) {
    dev_warn(&pfhwdev->hwdev.hwif->pdev->dev,
      "PF recv mgmt comm msg handle null, cmd: 0x%x\n",
      cmd);
   } else {
    pfhwdev->proc.info[cmd_idx].proc
     (&pfhwdev->hwdev, buf_in, in_size,
      buf_out, out_size);
   }

   return;
  }
 }

 dev_warn(&pfhwdev->hwdev.hwif->pdev->dev, "Received unknown mgmt cpu event: 0x%x\n",
   cmd);

 *out_size = 0;
}

/* pf fault report event */
static void pf_fault_event_handler(void *dev, void *buf_in, u16 in_size,
       void *buf_out, u16 *out_size)
{
 struct hinic_cmd_fault_event *fault_event = buf_in;
 struct hinic_hwdev *hwdev = dev;

 if (in_size != sizeof(*fault_event)) {
  dev_err(&hwdev->hwif->pdev->dev, "Invalid fault event report, length: %d, should be %zu\n",
   in_size, sizeof(*fault_event));
  return;
 }

 if (!hwdev->devlink_dev || IS_ERR_OR_NULL(hwdev->devlink_dev->hw_fault_reporter))
  return;

 devlink_health_report(hwdev->devlink_dev->hw_fault_reporter,
         "HW fatal error reported", &fault_event->event);
}

static void mgmt_watchdog_timeout_event_handler(void *dev,
      void *buf_in, u16 in_size,
      void *buf_out, u16 *out_size)
{
 struct hinic_mgmt_watchdog_info *watchdog_info = buf_in;
 struct hinic_hwdev *hwdev = dev;

 if (in_size != sizeof(*watchdog_info)) {
  dev_err(&hwdev->hwif->pdev->dev, "Invalid mgmt watchdog report, length: %d, should be %zu\n",
   in_size, sizeof(*watchdog_info));
  return;
 }

 if (!hwdev->devlink_dev || IS_ERR_OR_NULL(hwdev->devlink_dev->fw_fault_reporter))
  return;

 devlink_health_report(hwdev->devlink_dev->fw_fault_reporter,
         "FW fatal error reported", watchdog_info);
}

/**
 * init_pfhwdev - Initialize the extended components of PF
 * @pfhwdev: the HW device for PF
 *
 * Return 0 - success, negative - failure
 **/

static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev)
{
 struct hinic_hwdev *hwdev = &pfhwdev->hwdev;
 struct hinic_hwif *hwif = hwdev->hwif;
 struct pci_dev *pdev = hwif->pdev;
 int err;

 err = hinic_pf_to_mgmt_init(&pfhwdev->pf_to_mgmt, hwif);
 if (err) {
  dev_err(&pdev->dev, "Failed to initialize PF to MGMT channel\n");
  return err;
 }

 err = hinic_func_to_func_init(hwdev);
 if (err) {
  dev_err(&hwif->pdev->dev, "Failed to init mailbox\n");
  hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt);
  return err;
 }

 if (!HINIC_IS_VF(hwif)) {
  hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt,
        HINIC_MOD_L2NIC, pfhwdev,
        nic_mgmt_msg_handler);
  hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
        pfhwdev, comm_mgmt_msg_handler);
  hinic_comm_recv_mgmt_self_cmd_reg(pfhwdev,
        HINIC_COMM_CMD_FAULT_REPORT,
        pf_fault_event_handler);
  hinic_comm_recv_mgmt_self_cmd_reg
   (pfhwdev, HINIC_COMM_CMD_WATCHDOG_INFO,
    mgmt_watchdog_timeout_event_handler);
 } else {
  hinic_register_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC,
       nic_mgmt_msg_handler);
 }

 hinic_set_pf_action(hwif, HINIC_PF_MGMT_ACTIVE);
 hinic_devlink_register(hwdev->devlink_dev);
 return 0;
}

/**
 * free_pfhwdev - Free the extended components of PF
 * @pfhwdev: the HW device for PF
 **/

static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev)
{
 struct hinic_hwdev *hwdev = &pfhwdev->hwdev;

 hinic_devlink_unregister(hwdev->devlink_dev);
 hinic_set_pf_action(hwdev->hwif, HINIC_PF_MGMT_INIT);

 if (!HINIC_IS_VF(hwdev->hwif)) {
  hinic_comm_recv_mgmt_self_cmd_unreg(pfhwdev,
          HINIC_COMM_CMD_WATCHDOG_INFO);
  hinic_comm_recv_mgmt_self_cmd_unreg(pfhwdev,
          HINIC_COMM_CMD_FAULT_REPORT);
  hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt,
          HINIC_MOD_COMM);
  hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt,
          HINIC_MOD_L2NIC);
 } else {
  hinic_unregister_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC);
 }

 hinic_func_to_func_free(hwdev);

 hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt);
}

static int hinic_l2nic_reset(struct hinic_hwdev *hwdev)
{
 struct hinic_cmd_l2nic_reset l2nic_reset = {0};
 u16 out_size = sizeof(l2nic_reset);
 struct hinic_pfhwdev *pfhwdev;
 int err;

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

 l2nic_reset.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
 /* 0 represents standard l2nic reset flow */
 l2nic_reset.reset_flag = 0;

 err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
    HINIC_COMM_CMD_L2NIC_RESET, &l2nic_reset,
    sizeof(l2nic_reset), &l2nic_reset,
    &out_size, HINIC_MGMT_MSG_SYNC);
 if (err || !out_size || l2nic_reset.status) {
  dev_err(&hwdev->hwif->pdev->dev, "Failed to reset L2NIC resources, err: %d, status: 0x%x, out_size: 0x%x\n",
   err, l2nic_reset.status, out_size);
  return -EIO;
 }

 return 0;
}

static 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_credit_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
 * @devlink: the poniter of hinic devlink
 *
 * Return initialized NIC HW device
 *
 * Initialize the NIC HW device and return a pointer to it
 **/

struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev, struct devlink *devlink)
{
 struct hinic_pfhwdev *pfhwdev;
 struct hinic_hwdev *hwdev;
 struct hinic_hwif *hwif;
 int err, num_aeqs;

 hwif = devm_kzalloc(&pdev->dev, sizeof(*hwif), GFP_KERNEL);
 if (!hwif)
  return ERR_PTR(-ENOMEM);

 err = hinic_init_hwif(hwif, pdev);
 if (err) {
  dev_err(&pdev->dev, "Failed to init HW interface\n");
  return ERR_PTR(err);
 }

 pfhwdev = devm_kzalloc(&pdev->dev, sizeof(*pfhwdev), GFP_KERNEL);
 if (!pfhwdev) {
  err = -ENOMEM;
  goto err_pfhwdev_alloc;
 }

 hwdev = &pfhwdev->hwdev;
 hwdev->hwif = hwif;
 hwdev->devlink_dev = devlink_priv(devlink);
 hwdev->devlink_dev->hwdev = hwdev;

 err = init_msix(hwdev);
 if (err) {
  dev_err(&pdev->dev, "Failed to init msix\n");
  goto err_init_msix;
 }

 err = wait_for_outbound_state(hwdev);
 if (err) {
  dev_warn(&pdev->dev, "outbound - disabled, try again\n");
  hinic_outbound_state_set(hwif, HINIC_OUTBOUND_ENABLE);
 }

 num_aeqs = HINIC_HWIF_NUM_AEQS(hwif);

 err = hinic_aeqs_init(&hwdev->aeqs, hwif, num_aeqs,
         HINIC_DEFAULT_AEQ_LEN, HINIC_EQ_PAGE_SIZE,
         hwdev->msix_entries);
 if (err) {
  dev_err(&pdev->dev, "Failed to init async event queues\n");
  goto err_aeqs_init;
 }

 err = init_pfhwdev(pfhwdev);
 if (err) {
  dev_err(&pdev->dev, "Failed to init PF HW device\n");
  goto err_init_pfhwdev;
 }

 err = hinic_l2nic_reset(hwdev);
 if (err)
  goto err_l2nic_reset;

 err = get_dev_cap(hwdev);
 if (err) {
  dev_err(&pdev->dev, "Failed to get device capabilities\n");
  goto err_dev_cap;
 }

 mutex_init(&hwdev->func_to_io.nic_cfg.cfg_mutex);

 err = hinic_vf_func_init(hwdev);
 if (err) {
  dev_err(&pdev->dev, "Failed to init nic mbox\n");
  goto err_vf_func_init;
 }

 err = init_fw_ctxt(hwdev);
 if (err) {
  dev_err(&pdev->dev, "Failed to init function table\n");
  goto err_init_fw_ctxt;
 }

 err = set_resources_state(hwdev, HINIC_RES_ACTIVE);
 if (err) {
  dev_err(&pdev->dev, "Failed to set resources state\n");
  goto err_resources_state;
 }

 return hwdev;

err_resources_state:
err_init_fw_ctxt:
 hinic_vf_func_free(hwdev);
err_vf_func_init:
err_l2nic_reset:
err_dev_cap:
 free_pfhwdev(pfhwdev);

err_init_pfhwdev:
 hinic_aeqs_free(&hwdev->aeqs);

err_aeqs_init:
 disable_msix(hwdev);

err_init_msix:
err_pfhwdev_alloc:
 hinic_free_hwif(hwif);
 if (err > 0)
  err = -EIO;
 return ERR_PTR(err);
}

/**
 * hinic_free_hwdev - Free the NIC HW device
 * @hwdev: the NIC HW device
 **/

void hinic_free_hwdev(struct hinic_hwdev *hwdev)
{
 struct hinic_pfhwdev *pfhwdev = container_of(hwdev,
           struct hinic_pfhwdev,
           hwdev);

 set_resources_state(hwdev, HINIC_RES_CLEAN);

 hinic_vf_func_free(hwdev);

 free_pfhwdev(pfhwdev);

 hinic_aeqs_free(&hwdev->aeqs);

 disable_msix(hwdev);

 hinic_free_hwif(hwdev->hwif);
}

/**
 * hinic_hwdev_num_qps - return the number QPs available for use
 * @hwdev: the NIC HW device
 *
 * Return number QPs available for use
 **/

int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev)
{
 struct hinic_cap *nic_cap = &hwdev->nic_cap;

 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_rq - 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;
}

/**
 * hinic_hwdev_msix_cnt_set - clear message attribute counters for msix entry
 * @hwdev: the NIC HW device
 * @msix_index: msix_index
 *
 * Return 0 - Success, negative - Failure
 **/

int hinic_hwdev_msix_cnt_set(struct hinic_hwdev *hwdev, u16 msix_index)
{
 return hinic_msix_attr_cnt_clear(hwdev->hwif, msix_index);
}

/**
 * hinic_hwdev_msix_set - set message attribute for msix entry
 * @hwdev: the NIC HW device
 * @msix_index: msix_index
 * @pending_limit: the maximum pending interrupt events (unit 8)
 * @coalesc_timer: coalesc period for interrupt (unit 8 us)
 * @lli_timer_cfg: replenishing period for low latency credit (unit 8 us)
 * @lli_credit_limit: maximum credits for low latency msix messages (unit 8)
 * @resend_timer: maximum wait for resending msix (unit coalesc period)
 *
 * Return 0 - Success, negative - Failure
 **/

int hinic_hwdev_msix_set(struct hinic_hwdev *hwdev, u16 msix_index,
    u8 pending_limit, u8 coalesc_timer,
    u8 lli_timer_cfg, u8 lli_credit_limit,
    u8 resend_timer)
{
 return hinic_msix_attr_set(hwdev->hwif, msix_index,
       pending_limit, coalesc_timer,
       lli_timer_cfg, lli_credit_limit,
       resend_timer);
}

/**
 * hinic_hwdev_hw_ci_addr_set - set cons idx addr and attributes in HW for sq
 * @hwdev: the NIC HW device
 * @sq: send queue
 * @pending_limit: the maximum pending update ci events (unit 8)
 * @coalesc_timer: coalesc period for update ci (unit 8 us)
 *
 * Return 0 - Success, negative - Failure
 **/

int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq,
          u8 pending_limit, u8 coalesc_timer)
{
 struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq);
 struct hinic_hwif *hwif = hwdev->hwif;
 struct hinic_pfhwdev *pfhwdev;
 struct hinic_cmd_hw_ci hw_ci;

 hw_ci.dma_attr_off  = 0;
 hw_ci.pending_limit = pending_limit;
 hw_ci.coalesc_timer = coalesc_timer;

 hw_ci.msix_en = 1;
 hw_ci.msix_entry_idx = sq->msix_entry;

 hw_ci.func_idx = HINIC_HWIF_FUNC_IDX(hwif);

 hw_ci.sq_id = qp->q_id;

 hw_ci.ci_addr = ADDR_IN_4BYTES(sq->hw_ci_dma_addr);

 pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
 return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt,
     HINIC_MOD_COMM,
     HINIC_COMM_CMD_SQ_HI_CI_SET,
     &hw_ci, sizeof(hw_ci), NULL,
     NULL, HINIC_MGMT_MSG_SYNC);
}

/**
 * hinic_hwdev_set_msix_state- set msix state
 * @hwdev: the NIC HW device
 * @msix_index: IRQ corresponding index number
 * @flag: msix state
 *
 **/

void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index,
    enum hinic_msix_state flag)
{
 hinic_set_msix_state(hwdev->hwif, msix_index, flag);
}

int hinic_get_board_info(struct hinic_hwdev *hwdev,
    struct hinic_comm_board_info *board_info)
{
 u16 out_size = sizeof(*board_info);
 struct hinic_pfhwdev *pfhwdev;
 int err;

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

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

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

 return 0;
}

Messung V0.5
C=98 H=90 G=94

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge