// SPDX-License-Identifier: GPL-2.0 /* 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. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_csr.h" #include "hinic_hwif.h" #include "hinic_nic_io.h" #include "hinic_api_cmd.h" #include "hinic_mgmt.h" #include "hinic_mbox.h" #include "hinic_nic_cfg.h" #include "hinic_hwif.h" #include "hinic_mgmt_interface.h" #include "hinic_multi_host_mgmt.h" #define SLAVE_HOST_STATUS_CLEAR(host_id, val) \ ((val) & (~(1U << (host_id)))) #define SLAVE_HOST_STATUS_SET(host_id, enable) \ (((u8)(enable) & 1U) << (host_id)) #define SLAVE_HOST_STATUS_GET(host_id, val) (!!((val) & (1U << (host_id)))) void set_slave_host_enable(struct hinic_hwdev *hwdev, u8 host_id, bool enable) { u32 reg_val; if (HINIC_FUNC_TYPE(hwdev) != TYPE_PPF) return; reg_val = hinic_hwif_read_reg(hwdev->hwif, HINIC_MULT_HOST_SLAVE_STATUS_ADDR); reg_val = SLAVE_HOST_STATUS_CLEAR(host_id, reg_val); reg_val |= SLAVE_HOST_STATUS_SET(host_id, enable); hinic_hwif_write_reg(hwdev->hwif, HINIC_MULT_HOST_SLAVE_STATUS_ADDR, reg_val); sdk_info(hwdev->dev_hdl, "Set slave host %d status %d, reg value: 0x%x\n", host_id, enable, reg_val); } bool hinic_get_slave_host_enable(void *hwdev, u8 host_id) { u32 reg_val; struct hinic_hwdev *dev = hwdev; if (HINIC_FUNC_TYPE(dev) != TYPE_PPF) return false; reg_val = hinic_hwif_read_reg(dev->hwif, HINIC_MULT_HOST_SLAVE_STATUS_ADDR); return SLAVE_HOST_STATUS_GET(host_id, reg_val); } EXPORT_SYMBOL(hinic_get_slave_host_enable); void set_master_host_mbox_enable(struct hinic_hwdev *hwdev, bool enable) { u32 reg_val; if (!IS_MASTER_HOST(hwdev) || HINIC_FUNC_TYPE(hwdev) != TYPE_PPF) return; reg_val = hinic_hwif_read_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR); reg_val = MULTI_HOST_REG_CLEAR(reg_val, MASTER_MBX_STS); reg_val |= MULTI_HOST_REG_SET((u8)enable, MASTER_MBX_STS); hinic_hwif_write_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR, reg_val); sdk_info(hwdev->dev_hdl, "multi-host status %d, reg value: 0x%x\n", enable, reg_val); } bool hinic_get_master_host_mbox_enable(void *hwdev) { u32 reg_val; struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_SLAVE_HOST(dev) || HINIC_FUNC_TYPE(dev) == TYPE_VF) return true; reg_val = hinic_hwif_read_reg(dev->hwif, HINIC_HOST_MODE_ADDR); return !!MULTI_HOST_REG_GET(reg_val, MASTER_MBX_STS); } void set_func_host_mode(struct hinic_hwdev *hwdev, enum hinic_func_mode mode) { switch (mode) { case FUNC_MOD_MULTI_BM_MASTER: sdk_info(hwdev->dev_hdl, "Detect multi-host BM master host\n"); hwdev->func_mode = FUNC_MOD_MULTI_BM_MASTER; hwdev->feature_cap = HINIC_MULTI_BM_MASTER; break; case FUNC_MOD_MULTI_BM_SLAVE: sdk_info(hwdev->dev_hdl, "Detect multi-host BM slave host\n"); hwdev->func_mode = FUNC_MOD_MULTI_BM_SLAVE; hwdev->feature_cap = HINIC_MULTI_BM_SLAVE; break; case FUNC_MOD_MULTI_VM_MASTER: sdk_info(hwdev->dev_hdl, "Detect multi-host VM master host\n"); hwdev->func_mode = FUNC_MOD_MULTI_VM_MASTER; hwdev->feature_cap = HINIC_MULTI_VM_MASTER; break; case FUNC_MOD_MULTI_VM_SLAVE: sdk_info(hwdev->dev_hdl, "Detect multi-host VM slave host\n"); hwdev->func_mode = FUNC_MOD_MULTI_VM_SLAVE; hwdev->feature_cap = HINIC_MULTI_VM_SLAVE; break; default: hwdev->func_mode = FUNC_MOD_NORMAL_HOST; hwdev->feature_cap = HINIC_NORMAL_HOST_CAP; break; } } bool is_multi_vm_slave(void *hwdev) { struct hinic_hwdev *hw_dev = hwdev; if (!hwdev) return false; return (hw_dev->func_mode == FUNC_MOD_MULTI_VM_SLAVE) ? true : false; } int rectify_host_mode(struct hinic_hwdev *hwdev) { u16 cur_sdi_mode; int err; if (hwdev->board_info.board_type != HINIC_BOARD_TYPE_MULTI_HOST_ETH_25GE) return 0; sdk_info(hwdev->dev_hdl, "Rectify host mode, host_id: %d\n", hinic_pcie_itf_id(hwdev)); err = hinic_get_sdi_mode(hwdev, &cur_sdi_mode); if (err == HINIC_MGMT_CMD_UNSUPPORTED) cur_sdi_mode = HINIC_SDI_MODE_BM; else if (err) return err; switch (cur_sdi_mode) { case HINIC_SDI_MODE_BM: if (hinic_pcie_itf_id(hwdev) == 0) set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_MASTER); else set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_SLAVE); break; case HINIC_SDI_MODE_VM: if (hinic_pcie_itf_id(hwdev) == 0) set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_MASTER); else set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_SLAVE); break; default: sdk_warn(hwdev->dev_hdl, "Unknown sdi mode %d\n", cur_sdi_mode); break; } return 0; } void detect_host_mode_pre(struct hinic_hwdev *hwdev) { enum hinic_chip_mode chip_mode; /* all pf can set HOST_MODE REG, so don't trust HOST_MODE REG for host0, * get chip mode from mgmt cpu for host0 * VF have not right to read HOST_MODE REG, detect mode from board info */ if (hinic_pcie_itf_id(hwdev) == 0 || HINIC_FUNC_TYPE(hwdev) == TYPE_VF) { set_func_host_mode(hwdev, FUNC_MOD_NORMAL_HOST); return; } chip_mode = hinic_hwif_read_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR); switch (MULTI_HOST_REG_GET(chip_mode, CHIP_MODE)) { case CHIP_MODE_VMGW: set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_SLAVE); /* mbox has not initialized, set slave host disable */ set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); break; case CHIP_MODE_BMGW: set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_SLAVE); /* mbox has not initialized, set slave host disable */ set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); break; default: set_func_host_mode(hwdev, FUNC_MOD_NORMAL_HOST); break; } } int __mbox_to_host(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout, enum hinic_mbox_ack_type ack_type) { struct hinic_hwdev *mbox_hwdev = hwdev; u8 dst_host_func_idx; int err; if (!IS_MULTI_HOST(hwdev) || HINIC_IS_VF(hwdev)) return -EPERM; if (hinic_func_type(hwdev) == TYPE_PF) { down(&hwdev->ppf_sem); mbox_hwdev = hwdev->ppf_hwdev; if (!mbox_hwdev) { err = -EINVAL; goto release_lock; } if (!hinic_is_hwdev_mod_inited(mbox_hwdev, HINIC_HWDEV_MBOX_INITED)) { err = -EPERM; goto release_lock; } } if (!mbox_hwdev->chip_present_flag) { err = -EPERM; goto release_lock; } if (!hinic_get_master_host_mbox_enable(hwdev)) { sdk_err(hwdev->dev_hdl, "Master host not initialized\n"); err = -EFAULT; goto release_lock; } if (!mbox_hwdev->mhost_mgmt) { /* send to master host in default */ dst_host_func_idx = 0; } else { dst_host_func_idx = IS_MASTER_HOST(hwdev) ? mbox_hwdev->mhost_mgmt->shost_ppf_idx : mbox_hwdev->mhost_mgmt->mhost_ppf_idx; } if (ack_type == MBOX_ACK) err = hinic_mbox_to_host(mbox_hwdev, dst_host_func_idx, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); else err = hinic_mbox_to_func_no_ack(mbox_hwdev, dst_host_func_idx, mod, cmd, buf_in, in_size); release_lock: if (hinic_func_type(hwdev) == TYPE_PF) up(&hwdev->ppf_sem); return err; } int hinic_mbox_to_host_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { if (!hwdev) return -EINVAL; return __mbox_to_host((struct hinic_hwdev *)hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout, MBOX_ACK); } EXPORT_SYMBOL(hinic_mbox_to_host_sync); int hinic_mbox_to_host_no_ack(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size) { return __mbox_to_host(hwdev, mod, cmd, buf_in, in_size, NULL, NULL, 0, MBOX_NO_ACK); } static int __get_func_nic_state_from_pf(struct hinic_hwdev *hwdev, u16 glb_func_idx, u8 *en); int sw_func_pf_mbox_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; struct hinic_slave_func_nic_state *nic_state, *out_state; int err; switch (cmd) { case HINIC_SW_CMD_GET_SLAVE_FUNC_NIC_STATE: nic_state = buf_in; out_state = buf_out; *out_size = sizeof(*nic_state); /* find nic state in ppf func_nic_en bitmap */ err = __get_func_nic_state_from_pf(hwdev, nic_state->func_idx, &out_state->enable); if (err) out_state->status = 1; else out_state->status = 0; break; default: break; } return 0; } static int __master_host_sw_func_handler(struct hinic_hwdev *hwdev, u16 pf_idx, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_multi_host_mgmt *mhost_mgmt = hwdev->mhost_mgmt; struct register_slave_host *slave_host, *out_shost; int err = 0; if (!mhost_mgmt) return -ENXIO; switch (cmd) { case HINIC_SW_CMD_SLAVE_HOST_PPF_REGISTER: slave_host = buf_in; out_shost = buf_out; *out_size = sizeof(*slave_host); mhost_mgmt->shost_registered = true; mhost_mgmt->shost_host_idx = slave_host->host_id; mhost_mgmt->shost_ppf_idx = slave_host->ppf_idx; bitmap_copy((ulong *)out_shost->funcs_nic_en, mhost_mgmt->func_nic_en, HINIC_MAX_FUNCTIONS); sdk_info(hwdev->dev_hdl, "slave host register ppf, host_id: %d, ppf_idx: %d\n", slave_host->host_id, slave_host->ppf_idx); out_shost->status = 0; break; case HINIC_SW_CMD_SLAVE_HOST_PPF_UNREGISTER: slave_host = buf_in; mhost_mgmt->shost_registered = false; sdk_info(hwdev->dev_hdl, "slave host unregister ppf, host_id: %d, ppf_idx: %d\n", slave_host->host_id, slave_host->ppf_idx); *out_size = sizeof(*slave_host); ((struct register_slave_host *)buf_out)->status = 0; break; default: err = -EINVAL; break; } return err; } static int __event_set_func_nic_state(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_event_info event_info = {0}; struct hinic_mhost_nic_func_state nic_state = {0}; struct hinic_slave_func_nic_state *out_state, *func_nic_state = buf_in; event_info.type = HINIC_EVENT_MULTI_HOST_MGMT; event_info.mhost_mgmt.sub_cmd = HINIC_MHOST_NIC_STATE_CHANGE; event_info.mhost_mgmt.data = &nic_state; nic_state.func_idx = func_nic_state->func_idx; nic_state.enable = func_nic_state->enable; if (!hwdev->event_callback) return -EFAULT; hwdev->event_callback(hwdev->event_pri_handle, &event_info); *out_size = sizeof(*out_state); out_state = buf_out; out_state->status = nic_state.status; return nic_state.status; } static int multi_host_event_handler(struct hinic_hwdev *hwdev, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { int err; switch (cmd) { case HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE: err = __event_set_func_nic_state(hwdev, buf_in, in_size, buf_out, out_size); break; default: err = -EOPNOTSUPP; break; } return err; } static int sw_fwd_msg_to_vf(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_host_fwd_head *fwd_head; u16 fwd_head_len; void *msg; int err; fwd_head = buf_in; fwd_head_len = sizeof(struct hinic_host_fwd_head); msg = (void *)((u8 *)buf_in + fwd_head_len); err = hinic_mbox_ppf_to_vf(hwdev, fwd_head->mod, fwd_head->dst_glb_func_idx, fwd_head->cmd, msg, (in_size - fwd_head_len), buf_out, out_size, 0); if (err) nic_err(hwdev->dev_hdl, "Fwd msg to func %u failed, err: %d\n", fwd_head->dst_glb_func_idx, err); return err; } static int __slave_host_sw_func_handler(struct hinic_hwdev *hwdev, u16 pf_idx, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_multi_host_mgmt *mhost_mgmt = hwdev->mhost_mgmt; struct hinic_slave_func_nic_state *nic_state; int err = 0; if (!mhost_mgmt) return -ENXIO; switch (cmd) { case HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE: nic_state = buf_in; *out_size = sizeof(*nic_state); ((struct hinic_slave_func_nic_state *)buf_out)->status = 0; sdk_info(hwdev->dev_hdl, "slave func %d %s nic\n", nic_state->func_idx, nic_state->enable ? "register" : "unregister"); if (nic_state->enable) set_bit(nic_state->func_idx, mhost_mgmt->func_nic_en); else clear_bit(nic_state->func_idx, mhost_mgmt->func_nic_en); multi_host_event_handler(hwdev, cmd, buf_in, in_size, buf_out, out_size); break; case HINIC_SW_CMD_SEND_MSG_TO_VF: err = sw_fwd_msg_to_vf(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_SW_CMD_MIGRATE_READY: hinic_migrate_report(hwdev); break; default: err = -EINVAL; break; } return err; } int sw_func_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; int err; if (IS_MASTER_HOST(hwdev)) err = __master_host_sw_func_handler(hwdev, pf_idx, cmd, buf_in, in_size, buf_out, out_size); else if (IS_SLAVE_HOST(hwdev)) err = __slave_host_sw_func_handler(hwdev, pf_idx, cmd, buf_in, in_size, buf_out, out_size); else err = -EINVAL; if (err) sdk_err(hwdev->dev_hdl, "PPF process sw funcs cmd %d failed, err: %d\n", cmd, err); return err; } int __ppf_process_mbox_msg(struct hinic_hwdev *hwdev, u16 pf_idx, u16 vf_id, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { int err; if (IS_SLAVE_HOST(hwdev)) { err = hinic_mbox_to_host_sync(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, 0); if (err) sdk_err(hwdev->dev_hdl, "send to mpf failed, err: %d\n", err); } else if (IS_MASTER_HOST(hwdev)) { if (mod == HINIC_MOD_COMM && cmd == HINIC_MGMT_CMD_START_FLR) err = hinic_pf_to_mgmt_no_ack(hwdev, mod, cmd, buf_in, in_size); else err = hinic_pf_msg_to_mgmt_sync(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, 0U); if (err && err != HINIC_DEV_BUSY_ACTIVE_FW && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) sdk_err(hwdev->dev_hdl, "PF mbox common callback handler err: %d\n", err); } else { /* not support */ err = -EFAULT; } return err; } int hinic_ppf_process_mbox_msg(struct hinic_hwdev *hwdev, u16 pf_idx, u16 vf_id, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { bool same_host = false; int err = -EFAULT; /* modify same_host according to hinic_get_hw_pf_infos */ switch (hwdev->func_mode) { case FUNC_MOD_MULTI_VM_MASTER: case FUNC_MOD_MULTI_BM_MASTER: if (!same_host) err = __ppf_process_mbox_msg(hwdev, pf_idx, vf_id, mod, cmd, buf_in, in_size, buf_out, out_size); else sdk_warn(hwdev->dev_hdl, "Don't support ppf mbox message in BM master\n"); break; case FUNC_MOD_MULTI_VM_SLAVE: case FUNC_MOD_MULTI_BM_SLAVE: same_host = true; if (same_host) err = __ppf_process_mbox_msg(hwdev, pf_idx, vf_id, mod, cmd, buf_in, in_size, buf_out, out_size); else sdk_warn(hwdev->dev_hdl, "Receive control message from BM master, don't support for now\n"); break; default: sdk_warn(hwdev->dev_hdl, "Don't support ppf mbox message\n"); break; } return err; } int comm_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id, HINIC_MOD_COMM, cmd, buf_in, in_size, buf_out, out_size); } void comm_ppf_to_pf_handler(void *handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; sdk_err(hwdev->dev_hdl, "pf receive ppf common mbox msg, don't supported for now\n"); } int hilink_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id, HINIC_MOD_HILINK, cmd, buf_in, in_size, buf_out, out_size); } int hinic_nic_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id, HINIC_MOD_L2NIC, cmd, buf_in, in_size, buf_out, out_size); } void hinic_nic_ppf_to_pf_handler(void *handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; sdk_err(hwdev->dev_hdl, "ppf receive other pf l2nic mbox msg, don't supported for now\n"); } int hinic_register_slave_ppf(struct hinic_hwdev *hwdev, bool registered) { struct register_slave_host host_info = {0}; u16 out_size = sizeof(host_info); u8 cmd; int err; if (!IS_SLAVE_HOST(hwdev)) return -EINVAL; cmd = registered ? HINIC_SW_CMD_SLAVE_HOST_PPF_REGISTER : HINIC_SW_CMD_SLAVE_HOST_PPF_UNREGISTER; host_info.host_id = hinic_pcie_itf_id(hwdev); host_info.ppf_idx = hinic_ppf_idx(hwdev); err = hinic_mbox_to_host_sync(hwdev, HINIC_MOD_SW_FUNC, cmd, &host_info, sizeof(host_info), &host_info, &out_size, 0); if (err || !out_size || host_info.status) { sdk_err(hwdev->dev_hdl, "Failed to %s slave host, err: %d, out_size: 0x%x, status: 0x%x\n", registered ? "register" : "unregister", err, out_size, host_info.status); return -EFAULT; } bitmap_copy(hwdev->mhost_mgmt->func_nic_en, (ulong *)host_info.funcs_nic_en, HINIC_MAX_FUNCTIONS); return 0; } static int get_host_id_by_func_id(struct hinic_hwdev *hwdev, u16 func_idx, u8 *host_id) { struct hinic_hw_pf_infos *pf_infos; u16 vf_id_start, vf_id_end; int i; if (!hwdev || !host_id || !hwdev->mhost_mgmt) return -EINVAL; pf_infos = &hwdev->mhost_mgmt->pf_infos; for (i = 0; i < pf_infos->num_pfs; i++) { if (func_idx == pf_infos->infos[i].glb_func_idx) { *host_id = pf_infos->infos[i].itf_idx; return 0; } vf_id_start = pf_infos->infos[i].glb_pf_vf_offset + 1; vf_id_end = pf_infos->infos[i].glb_pf_vf_offset + pf_infos->infos[i].max_vfs; if (func_idx >= vf_id_start && func_idx <= vf_id_end) { *host_id = pf_infos->infos[i].itf_idx; return 0; } } return -EFAULT; } int set_slave_func_nic_state(struct hinic_hwdev *hwdev, u16 func_idx, u8 en) { struct hinic_slave_func_nic_state nic_state = {0}; u16 out_size = sizeof(nic_state); int err; nic_state.func_idx = func_idx; nic_state.enable = en; err = hinic_mbox_to_host_sync(hwdev, HINIC_MOD_SW_FUNC, HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE, &nic_state, sizeof(nic_state), &nic_state, &out_size, 0); if (err == MBOX_ERRCODE_UNKNOWN_DES_FUNC) { sdk_warn(hwdev->dev_hdl, "Can not notify func %d nic state because slave host not initialized\n", func_idx); } else if (err || !out_size || nic_state.status) { sdk_err(hwdev->dev_hdl, "Failed to set slave host functions nic state, err: %d, out_size: 0x%x, status: 0x%x\n", err, out_size, nic_state.status); return -EFAULT; } return 0; } int hinic_set_func_nic_state(void *hwdev, struct hinic_func_nic_state *state) { struct hinic_hwdev *ppf_hwdev = hwdev; struct hinic_multi_host_mgmt *mhost_mgmt; u8 host_id = 0; bool host_enable; int err; int old_state; if (!hwdev || !state) return -EINVAL; if (hinic_func_type(hwdev) != TYPE_PPF) ppf_hwdev = ((struct hinic_hwdev *)hwdev)->ppf_hwdev; if (!ppf_hwdev || !IS_MASTER_HOST(ppf_hwdev)) return -EINVAL; mhost_mgmt = ppf_hwdev->mhost_mgmt; if (!mhost_mgmt || state->func_idx >= HINIC_MAX_FUNCTIONS) return -EINVAL; old_state = test_bit(state->func_idx, mhost_mgmt->func_nic_en) ? 1 : 0; if (state->state == HINIC_FUNC_NIC_DEL) clear_bit(state->func_idx, mhost_mgmt->func_nic_en); else if (state->state == HINIC_FUNC_NIC_ADD) set_bit(state->func_idx, mhost_mgmt->func_nic_en); else return -EINVAL; err = get_host_id_by_func_id(ppf_hwdev, state->func_idx, &host_id); if (err) { sdk_err(ppf_hwdev->dev_hdl, "Failed to get function %d host id, err: %d\n", state->func_idx, err); old_state ? set_bit(state->func_idx, mhost_mgmt->func_nic_en) : clear_bit(state->func_idx, mhost_mgmt->func_nic_en); return -EFAULT; } host_enable = hinic_get_slave_host_enable(hwdev, host_id); sdk_info(ppf_hwdev->dev_hdl, "Set slave host %d(status: %d) func %d %s nic\n", host_id, host_enable, state->func_idx, state->state ? "enable" : "disable"); if (!host_enable) return 0; /* notify slave host */ err = set_slave_func_nic_state(hwdev, state->func_idx, state->state); if (err) { old_state ? set_bit(state->func_idx, mhost_mgmt->func_nic_en) : clear_bit(state->func_idx, mhost_mgmt->func_nic_en); return err; } return 0; } EXPORT_SYMBOL(hinic_set_func_nic_state); static int __get_func_nic_state_from_pf(struct hinic_hwdev *hwdev, u16 glb_func_idx, u8 *en) { struct hinic_multi_host_mgmt *mhost_mgmt; struct hinic_hwdev *ppf_hwdev = hwdev; if (hinic_func_type(hwdev) != TYPE_PPF) ppf_hwdev = ((struct hinic_hwdev *)hwdev)->ppf_hwdev; if (!ppf_hwdev || !ppf_hwdev->mhost_mgmt) return -EFAULT; mhost_mgmt = ppf_hwdev->mhost_mgmt; *en = !!(test_bit(glb_func_idx, mhost_mgmt->func_nic_en)); sdk_info(ppf_hwdev->dev_hdl, "slave host func %d nic %d\n", glb_func_idx, *en); return 0; } int hinic_get_func_nic_enable(void *hwdev, u16 glb_func_idx, bool *en) { struct hinic_slave_func_nic_state nic_state = {0}; u16 out_size = sizeof(nic_state); u8 nic_en; int err; if (!hwdev || !en) return -EINVAL; /*if card mode is OVS, VFs donot need attach_uld, so return false.*/ if (!IS_SLAVE_HOST((struct hinic_hwdev *)hwdev)) { if (hinic_func_type(hwdev) == TYPE_VF && hinic_support_ovs(hwdev, NULL)) { *en = false; } else { *en = true; } return 0; } if (hinic_func_type(hwdev) == TYPE_VF) { nic_state.func_idx = glb_func_idx; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_SW_FUNC, HINIC_SW_CMD_GET_SLAVE_FUNC_NIC_STATE, &nic_state, sizeof(nic_state), &nic_state, &out_size, 0); if (err || !out_size || nic_state.status) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get func %d nic state, err: %d, out_size: 0x%x, status: 0x%x\n", glb_func_idx, err, out_size, nic_state.status); return -EFAULT; } *en = !!nic_state.enable; return 0; } /* pf in slave host should be probe in CHIP_MODE_VMGW * mode for pxe install */ if (IS_VM_SLAVE_HOST((struct hinic_hwdev *)hwdev)) { *en = true; return 0; } /* pf/ppf get function nic state in sdk diretly */ err = __get_func_nic_state_from_pf(hwdev, glb_func_idx, &nic_en); if (err) return err; *en = !!nic_en; return 0; } int hinic_multi_host_mgmt_init(struct hinic_hwdev *hwdev) { int err; if (!IS_MULTI_HOST(hwdev) || !HINIC_IS_PPF(hwdev)) return 0; hwdev->mhost_mgmt = kzalloc(sizeof(*hwdev->mhost_mgmt), GFP_KERNEL); if (!hwdev->mhost_mgmt) { sdk_err(hwdev->dev_hdl, "Failed to alloc multi-host mgmt memory\n"); return -ENOMEM; } err = hinic_get_hw_pf_infos(hwdev, &hwdev->mhost_mgmt->pf_infos); if (err) goto out_free_mhost_mgmt; /* master ppf idx fix to 0 */ hwdev->mhost_mgmt->mhost_ppf_idx = 0; if (IS_BMGW_MASTER_HOST(hwdev) || IS_BMGW_SLAVE_HOST(hwdev)) { /* fix slave host ppf 6 and host 2 in bmwg mode */ hwdev->mhost_mgmt->shost_ppf_idx = 6; hwdev->mhost_mgmt->shost_host_idx = 2; } else { hwdev->mhost_mgmt->shost_ppf_idx = 7; hwdev->mhost_mgmt->shost_host_idx = 2; } hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_COMM, comm_ppf_mbox_handler); hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_L2NIC, hinic_nic_ppf_mbox_handler); hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_HILINK, hilink_ppf_mbox_handler); hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_SW_FUNC, sw_func_ppf_mbox_handler); bitmap_zero(hwdev->mhost_mgmt->func_nic_en, HINIC_MAX_FUNCTIONS); /* Slave host: * register slave host ppf functions * Get function's nic state */ if (IS_SLAVE_HOST(hwdev)) { /* PXE don't support to receive mbox from master host */ set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), true); if ((IS_VM_SLAVE_HOST(hwdev) && hinic_get_master_host_mbox_enable(hwdev)) || IS_BMGW_SLAVE_HOST(hwdev)) { err = hinic_register_slave_ppf(hwdev, true); if (err) { set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); goto out_free_mhost_mgmt; } } } else { /* slave host can send message to mgmt cpu after setup master * mbox */ set_master_host_mbox_enable(hwdev, true); } return 0; out_free_mhost_mgmt: kfree(hwdev->mhost_mgmt); hwdev->mhost_mgmt = NULL; return err; } int hinic_multi_host_mgmt_free(struct hinic_hwdev *hwdev) { if (!IS_MULTI_HOST(hwdev) || !HINIC_IS_PPF(hwdev)) return 0; if (IS_SLAVE_HOST(hwdev)) { hinic_register_slave_ppf(hwdev, false); set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); } else { set_master_host_mbox_enable(hwdev, false); } hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_COMM); hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_L2NIC); hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_HILINK); hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_SW_FUNC); kfree(hwdev->mhost_mgmt); hwdev->mhost_mgmt = NULL; return 0; }