Commit 20d2e88c authored by Alexander Duyck's avatar Alexander Duyck Committed by Jakub Kicinski

eth: fbnic: Add initial messaging to notify FW of our presence

After the driver loads we need to get some initial capabilities from the
firmware to determine what the device is capable of and what functionality
needs to be enabled. Specifically we receive information about the current
state of the link and if a BMC is present.

After that when we bring the interface up we will need the ability to take
ownership from the FW. To do that we will need to notify it that we are
taking control before we start configuring the traffic classifier and MAC.

Once we have ownership we need to notify the firmware that we are still
present and active. To do that we will send a regular heartbeat to the FW.
If the FW doesn't receive the heartbeat in a timely fashion it will retake
control of the RPC and MAC and assume that the host has gone offline.
Signed-off-by: default avatarAlexander Duyck <alexanderduyck@fb.com>
Link: https://patch.msgid.link/172079939458.1778861.8966209942099133957.stgit@ahduyck-xeon-server.home.arpaSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 0cb4c0a1
......@@ -26,9 +26,14 @@ struct fbnic_dev {
struct delayed_work service_task;
struct fbnic_fw_mbx mbx[FBNIC_IPC_MBX_INDICES];
struct fbnic_fw_cap fw_cap;
/* Lock protecting Tx Mailbox queue to prevent possible races */
spinlock_t fw_tx_lock;
unsigned long last_heartbeat_request;
unsigned long last_heartbeat_response;
u8 fw_heartbeat_enabled;
u64 dsn;
u32 mps;
u32 readrq;
......
......@@ -12,6 +12,14 @@
#define DESC_BIT(nr) BIT_ULL(nr)
#define DESC_GENMASK(h, l) GENMASK_ULL(h, l)
/* Defines the minimum firmware version required by the driver */
#define MIN_FW_MAJOR_VERSION 0
#define MIN_FW_MINOR_VERSION 10
#define MIN_FW_BUILD_VERSION 6
#define MIN_FW_VERSION_CODE (MIN_FW_MAJOR_VERSION * (1u << 24) + \
MIN_FW_MINOR_VERSION * (1u << 16) + \
MIN_FW_BUILD_VERSION)
#define PCI_DEVICE_ID_META_FBNIC_ASIC 0x0013
#define FBNIC_CLOCK_FREQ (600 * (1000 * 1000))
......
This diff is collapsed.
......@@ -4,6 +4,7 @@
#ifndef _FBNIC_FW_H_
#define _FBNIC_FW_H_
#include <linux/if_ether.h>
#include <linux/types.h>
struct fbnic_dev;
......@@ -17,10 +18,94 @@ struct fbnic_fw_mbx {
} buf_info[FBNIC_IPC_MBX_DESC_LEN];
};
// FW_VER_MAX_SIZE must match ETHTOOL_FWVERS_LEN
#define FBNIC_FW_VER_MAX_SIZE 32
// Formatted version is in the format XX.YY.ZZ_RRR_COMMIT
#define FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE (FBNIC_FW_VER_MAX_SIZE - 13)
#define FBNIC_FW_LOG_MAX_SIZE 256
struct fbnic_fw_ver {
u32 version;
char commit[FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE];
};
struct fbnic_fw_cap {
struct {
struct fbnic_fw_ver mgmt, bootloader;
} running;
struct {
struct fbnic_fw_ver mgmt, bootloader, undi;
} stored;
u8 active_slot;
u8 bmc_mac_addr[4][ETH_ALEN];
u8 bmc_present : 1;
u8 all_multi : 1;
u8 link_speed;
u8 link_fec;
};
void fbnic_mbx_init(struct fbnic_dev *fbd);
void fbnic_mbx_clean(struct fbnic_dev *fbd);
void fbnic_mbx_poll(struct fbnic_dev *fbd);
int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd);
void fbnic_mbx_flush_tx(struct fbnic_dev *fbd);
int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership);
int fbnic_fw_init_heartbeat(struct fbnic_dev *fbd, bool poll);
void fbnic_fw_check_heartbeat(struct fbnic_dev *fbd);
#define fbnic_mk_full_fw_ver_str(_rev_id, _delim, _commit, _str) \
do { \
const u32 __rev_id = _rev_id; \
snprintf(_str, sizeof(_str), "%02lu.%02lu.%02lu-%03lu%s%s", \
FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_MAJOR, __rev_id), \
FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_MINOR, __rev_id), \
FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_PATCH, __rev_id), \
FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_BUILD, __rev_id), \
_delim, _commit); \
} while (0)
#define fbnic_mk_fw_ver_str(_rev_id, _str) \
fbnic_mk_full_fw_ver_str(_rev_id, "", "", _str)
#define FW_HEARTBEAT_PERIOD (10 * HZ)
enum {
FBNIC_TLV_MSG_ID_HOST_CAP_REQ = 0x10,
FBNIC_TLV_MSG_ID_FW_CAP_RESP = 0x11,
FBNIC_TLV_MSG_ID_OWNERSHIP_REQ = 0x12,
FBNIC_TLV_MSG_ID_OWNERSHIP_RESP = 0x13,
FBNIC_TLV_MSG_ID_HEARTBEAT_REQ = 0x14,
FBNIC_TLV_MSG_ID_HEARTBEAT_RESP = 0x15,
};
#define FBNIC_FW_CAP_RESP_VERSION_MAJOR CSR_GENMASK(31, 24)
#define FBNIC_FW_CAP_RESP_VERSION_MINOR CSR_GENMASK(23, 16)
#define FBNIC_FW_CAP_RESP_VERSION_PATCH CSR_GENMASK(15, 8)
#define FBNIC_FW_CAP_RESP_VERSION_BUILD CSR_GENMASK(7, 0)
enum {
FBNIC_FW_CAP_RESP_VERSION = 0x0,
FBNIC_FW_CAP_RESP_BMC_PRESENT = 0x1,
FBNIC_FW_CAP_RESP_BMC_MAC_ADDR = 0x2,
FBNIC_FW_CAP_RESP_BMC_MAC_ARRAY = 0x3,
FBNIC_FW_CAP_RESP_STORED_VERSION = 0x4,
FBNIC_FW_CAP_RESP_ACTIVE_FW_SLOT = 0x5,
FBNIC_FW_CAP_RESP_VERSION_COMMIT_STR = 0x6,
FBNIC_FW_CAP_RESP_BMC_ALL_MULTI = 0x8,
FBNIC_FW_CAP_RESP_FW_STATE = 0x9,
FBNIC_FW_CAP_RESP_FW_LINK_SPEED = 0xa,
FBNIC_FW_CAP_RESP_FW_LINK_FEC = 0xb,
FBNIC_FW_CAP_RESP_STORED_COMMIT_STR = 0xc,
FBNIC_FW_CAP_RESP_CMRT_VERSION = 0xd,
FBNIC_FW_CAP_RESP_STORED_CMRT_VERSION = 0xe,
FBNIC_FW_CAP_RESP_CMRT_COMMIT_STR = 0xf,
FBNIC_FW_CAP_RESP_STORED_CMRT_COMMIT_STR = 0x10,
FBNIC_FW_CAP_RESP_UEFI_VERSION = 0x11,
FBNIC_FW_CAP_RESP_UEFI_COMMIT_STR = 0x12,
FBNIC_FW_CAP_RESP_MSG_MAX
};
enum {
FBNIC_FW_OWNERSHIP_FLAG = 0x0,
FBNIC_FW_OWNERSHIP_MSG_MAX
};
#endif /* _FBNIC_FW_H_ */
......@@ -11,6 +11,7 @@
int __fbnic_open(struct fbnic_net *fbn)
{
struct fbnic_dev *fbd = fbn->fbd;
int err;
err = fbnic_alloc_napi_vectors(fbn);
......@@ -31,7 +32,22 @@ int __fbnic_open(struct fbnic_net *fbn)
if (err)
goto free_resources;
/* Send ownership message and flush to verify FW has seen it */
err = fbnic_fw_xmit_ownership_msg(fbd, true);
if (err) {
dev_warn(fbd->dev,
"Error %d sending host ownership message to the firmware\n",
err);
goto free_resources;
}
err = fbnic_fw_init_heartbeat(fbd, false);
if (err)
goto release_ownership;
return 0;
release_ownership:
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
free_resources:
fbnic_free_resources(fbn);
free_napi_vectors:
......@@ -57,6 +73,8 @@ static int fbnic_stop(struct net_device *netdev)
fbnic_down(fbn);
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
fbnic_free_resources(fbn);
fbnic_free_napi_vectors(fbn);
......
......@@ -158,6 +158,30 @@ void fbnic_down(struct fbnic_net *fbn)
fbnic_flush(fbn);
}
static void fbnic_health_check(struct fbnic_dev *fbd)
{
struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
/* As long as the heart is beating the FW is healty */
if (fbd->fw_heartbeat_enabled)
return;
/* If the Tx mailbox still has messages sitting in it then there likely
* isn't anything we can do. We will wait until the mailbox is empty to
* report the fault so we can collect the crashlog.
*/
if (tx_mbx->head != tx_mbx->tail)
return;
/* TBD: Need to add a more thorough recovery here.
* Specifically I need to verify what all the firmware will have
* changed since we had setup and it rebooted. May just need to
* perform a down/up. For now we will just reclaim ownership so
* the heartbeat can catch the next fault.
*/
fbnic_fw_xmit_ownership_msg(fbd, true);
}
static void fbnic_service_task(struct work_struct *work)
{
struct fbnic_dev *fbd = container_of(to_delayed_work(work),
......@@ -165,6 +189,10 @@ static void fbnic_service_task(struct work_struct *work)
rtnl_lock();
fbnic_fw_check_heartbeat(fbd);
fbnic_health_check(fbd);
if (netif_running(fbd->netdev))
schedule_delayed_work(&fbd->service_task, HZ);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment