diff options
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic.h | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_csr.h | 27 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c | 29 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_fw.c | 179 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_fw.h | 36 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c | 123 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_fw_log.h | 45 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_pci.c | 21 |
9 files changed, 451 insertions, 13 deletions
diff --git a/drivers/net/ethernet/meta/fbnic/Makefile b/drivers/net/ethernet/meta/fbnic/Makefile index 0dbc634adb4b..15e8ff649615 100644 --- a/drivers/net/ethernet/meta/fbnic/Makefile +++ b/drivers/net/ethernet/meta/fbnic/Makefile @@ -12,6 +12,7 @@ fbnic-y := fbnic_csr.o \ fbnic_devlink.o \ fbnic_ethtool.o \ fbnic_fw.o \ + fbnic_fw_log.o \ fbnic_hw_stats.o \ fbnic_hwmon.o \ fbnic_irq.o \ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic.h b/drivers/net/ethernet/meta/fbnic/fbnic.h index 65815d4f379e..c376e06880c9 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic.h @@ -12,6 +12,7 @@ #include "fbnic_csr.h" #include "fbnic_fw.h" +#include "fbnic_fw_log.h" #include "fbnic_hw_stats.h" #include "fbnic_mac.h" #include "fbnic_rpc.h" @@ -85,6 +86,8 @@ struct fbnic_dev { /* Lock protecting access to hw_stats */ spinlock_t hw_stats_lock; + + struct fbnic_fw_log fw_log; }; /* Reserve entry 0 in the MSI-X "others" array until we have filled all diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h index 9c89d5378668..a81db842aa53 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h @@ -12,13 +12,28 @@ #define DESC_BIT(nr) BIT_ULL(nr) #define DESC_GENMASK(h, l) GENMASK_ULL(h, l) +#define FW_VER_CODE(_major, _minor, _patch, _build) ( \ + FIELD_PREP(FBNIC_FW_CAP_RESP_VERSION_MAJOR, _major) | \ + FIELD_PREP(FBNIC_FW_CAP_RESP_VERSION_MINOR, _minor) | \ + FIELD_PREP(FBNIC_FW_CAP_RESP_VERSION_PATCH, _patch) | \ + FIELD_PREP(FBNIC_FW_CAP_RESP_VERSION_BUILD, _build)) + /* 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 MIN_FW_VER_CODE FW_VER_CODE(0, 10, 6, 0) + +/* Defines the minimum firmware version required for firmware logs */ +#define MIN_FW_VER_CODE_LOG FW_VER_CODE(0, 12, 9, 0) + +/* Driver can request that firmware sends all cached logs in bulk. This + * feature was enabled on older firmware however firmware has a bug + * which attempted to send 30 messages per mbx message which caused an + * overflow flooding the mailbox. This results in a kernel warning + * related to corrupt mailbox messages. + * + * If firmware is new enough only request sending historical logs when + * the log buffer is empty to prevent duplicate logs. + */ +#define MIN_FW_VER_CODE_HIST FW_VER_CODE(25, 5, 7, 0) #define PCI_DEVICE_ID_META_FBNIC_ASIC 0x0013 diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c b/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c index e8f2d7f2d962..b7238dd967fe 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c @@ -170,6 +170,33 @@ static int fbnic_dbg_ipo_dst_show(struct seq_file *s, void *v) } DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_ipo_dst); +static int fbnic_dbg_fw_log_show(struct seq_file *s, void *v) +{ + struct fbnic_dev *fbd = s->private; + struct fbnic_fw_log_entry *entry; + unsigned long flags; + + if (!fbnic_fw_log_ready(fbd)) + return -ENXIO; + + spin_lock_irqsave(&fbd->fw_log.lock, flags); + + list_for_each_entry_reverse(entry, &fbd->fw_log.entries, list) { + seq_printf(s, FBNIC_FW_LOG_FMT, entry->index, + (entry->timestamp / (MSEC_PER_SEC * 60 * 60 * 24)), + (entry->timestamp / (MSEC_PER_SEC * 60 * 60)) % 24, + ((entry->timestamp / (MSEC_PER_SEC * 60) % 60)), + ((entry->timestamp / MSEC_PER_SEC) % 60), + (entry->timestamp % MSEC_PER_SEC), + entry->msg); + } + + spin_unlock_irqrestore(&fbd->fw_log.lock, flags); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_fw_log); + static int fbnic_dbg_pcie_stats_show(struct seq_file *s, void *v) { struct fbnic_dev *fbd = s->private; @@ -222,6 +249,8 @@ void fbnic_dbg_fbd_init(struct fbnic_dev *fbd) &fbnic_dbg_ipo_src_fops); debugfs_create_file("ipo_dst", 0400, fbd->dbg_fbd, fbd, &fbnic_dbg_ipo_dst_fops); + debugfs_create_file("fw_log", 0400, fbd->dbg_fbd, fbd, + &fbnic_dbg_fw_log_fops); } void fbnic_dbg_fbd_exit(struct fbnic_dev *fbd) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c index 1d220d8369e7..0c55be7d2547 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c @@ -573,16 +573,15 @@ static int fbnic_fw_parse_cap_resp(void *opaque, struct fbnic_tlv_msg **results) if (!fbd->fw_cap.running.mgmt.version) return -EINVAL; - if (fbd->fw_cap.running.mgmt.version < MIN_FW_VERSION_CODE) { + if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE) { + char required_ver[FBNIC_FW_VER_MAX_SIZE]; char running_ver[FBNIC_FW_VER_MAX_SIZE]; fbnic_mk_fw_ver_str(fbd->fw_cap.running.mgmt.version, running_ver); - dev_err(fbd->dev, "Device firmware version(%s) is older than minimum required version(%02d.%02d.%02d)\n", - running_ver, - MIN_FW_MAJOR_VERSION, - MIN_FW_MINOR_VERSION, - MIN_FW_BUILD_VERSION); + fbnic_mk_fw_ver_str(MIN_FW_VER_CODE, required_ver); + dev_err(fbd->dev, "Device firmware version(%s) is older than minimum required version(%s)\n", + running_ver, required_ver); /* Disable TX mailbox to prevent card use until firmware is * updated. */ @@ -1035,6 +1034,169 @@ msg_err: return err; } +static const struct fbnic_tlv_index fbnic_fw_log_req_index[] = { + FBNIC_TLV_ATTR_U32(FBNIC_FW_LOG_MSEC), + FBNIC_TLV_ATTR_U64(FBNIC_FW_LOG_INDEX), + FBNIC_TLV_ATTR_STRING(FBNIC_FW_LOG_MSG, FBNIC_FW_LOG_MAX_SIZE), + FBNIC_TLV_ATTR_U32(FBNIC_FW_LOG_LENGTH), + FBNIC_TLV_ATTR_ARRAY(FBNIC_FW_LOG_MSEC_ARRAY), + FBNIC_TLV_ATTR_ARRAY(FBNIC_FW_LOG_INDEX_ARRAY), + FBNIC_TLV_ATTR_ARRAY(FBNIC_FW_LOG_MSG_ARRAY), + FBNIC_TLV_ATTR_LAST +}; + +static int fbnic_fw_process_log_array(struct fbnic_tlv_msg **results, + u16 length, u16 arr_type_idx, + u16 attr_type_idx, + struct fbnic_tlv_msg **tlv_array_out) +{ + struct fbnic_tlv_msg *attr; + int attr_len; + int err; + + if (!results[attr_type_idx]) + return -EINVAL; + + tlv_array_out[0] = results[attr_type_idx]; + + if (!length) + return 0; + + if (!results[arr_type_idx]) + return -EINVAL; + + attr = results[arr_type_idx]; + attr_len = le16_to_cpu(attr->hdr.len) / sizeof(u32) - 1; + err = fbnic_tlv_attr_parse_array(&attr[1], attr_len, &tlv_array_out[1], + fbnic_fw_log_req_index, + attr_type_idx, + length); + if (err) + return err; + + return 0; +} + +static int fbnic_fw_parse_logs(struct fbnic_dev *fbd, + struct fbnic_tlv_msg **msec_tlv, + struct fbnic_tlv_msg **index_tlv, + struct fbnic_tlv_msg **log_tlv, + int count) +{ + int i; + + for (i = 0; i < count; i++) { + char log[FBNIC_FW_LOG_MAX_SIZE]; + ssize_t len; + u64 index; + u32 msec; + int err; + + if (!msec_tlv[i] || !index_tlv[i] || !log_tlv[i]) { + dev_warn(fbd->dev, "Received log message with missing attributes!\n"); + return -EINVAL; + } + + index = fbnic_tlv_attr_get_signed(index_tlv[i], 0); + msec = fbnic_tlv_attr_get_signed(msec_tlv[i], 0); + len = fbnic_tlv_attr_get_string(log_tlv[i], log, + FBNIC_FW_LOG_MAX_SIZE); + if (len < 0) + return len; + + err = fbnic_fw_log_write(fbd, index, msec, log); + if (err) + return err; + } + + return 0; +} + +static int fbnic_fw_parse_log_req(void *opaque, + struct fbnic_tlv_msg **results) +{ + struct fbnic_tlv_msg *index_tlv[FBNIC_FW_MAX_LOG_HISTORY]; + struct fbnic_tlv_msg *msec_tlv[FBNIC_FW_MAX_LOG_HISTORY]; + struct fbnic_tlv_msg *log_tlv[FBNIC_FW_MAX_LOG_HISTORY]; + struct fbnic_dev *fbd = opaque; + u16 length; + int err; + + length = fta_get_uint(results, FBNIC_FW_LOG_LENGTH); + if (length >= FBNIC_FW_MAX_LOG_HISTORY) + return -E2BIG; + + err = fbnic_fw_process_log_array(results, length, + FBNIC_FW_LOG_MSEC_ARRAY, + FBNIC_FW_LOG_MSEC, msec_tlv); + if (err) + return err; + + err = fbnic_fw_process_log_array(results, length, + FBNIC_FW_LOG_INDEX_ARRAY, + FBNIC_FW_LOG_INDEX, index_tlv); + if (err) + return err; + + err = fbnic_fw_process_log_array(results, length, + FBNIC_FW_LOG_MSG_ARRAY, + FBNIC_FW_LOG_MSG, log_tlv); + if (err) + return err; + + err = fbnic_fw_parse_logs(fbd, msec_tlv, index_tlv, log_tlv, + length + 1); + if (err) + return err; + + return 0; +} + +int fbnic_fw_xmit_send_logs(struct fbnic_dev *fbd, bool enable, + bool send_log_history) +{ + struct fbnic_tlv_msg *msg; + int err; + + if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE_LOG) { + dev_warn(fbd->dev, "Firmware version is too old to support firmware logs!\n"); + return -EOPNOTSUPP; + } + + msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_LOG_SEND_LOGS_REQ); + if (!msg) + return -ENOMEM; + + if (enable) { + err = fbnic_tlv_attr_put_flag(msg, FBNIC_SEND_LOGS); + if (err) + goto free_message; + + /* Report request for version 1 of logs */ + err = fbnic_tlv_attr_put_int(msg, FBNIC_SEND_LOGS_VERSION, + FBNIC_FW_LOG_VERSION); + if (err) + goto free_message; + + if (send_log_history) { + err = fbnic_tlv_attr_put_flag(msg, + FBNIC_SEND_LOGS_HISTORY); + if (err) + goto free_message; + } + } + + err = fbnic_mbx_map_tlv_msg(fbd, msg); + if (err) + goto free_message; + + return 0; + +free_message: + free_page((unsigned long)msg); + return err; +} + static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = { FBNIC_TLV_PARSER(FW_CAP_RESP, fbnic_fw_cap_resp_index, fbnic_fw_parse_cap_resp), @@ -1054,6 +1216,9 @@ static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = { FBNIC_TLV_PARSER(TSENE_READ_RESP, fbnic_tsene_read_resp_index, fbnic_fw_parse_tsene_read_resp), + FBNIC_TLV_PARSER(LOG_MSG_REQ, + fbnic_fw_log_req_index, + fbnic_fw_parse_log_req), FBNIC_TLV_MSG_ERROR }; @@ -1167,7 +1332,7 @@ int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd) * to indicate we entered the polling state waiting for a response */ for (fbd->fw_cap.running.mgmt.version = 1; - fbd->fw_cap.running.mgmt.version < MIN_FW_VERSION_CODE;) { + fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE;) { if (!tx_mbx->ready) err = -ENODEV; if (err) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h index 555b231b38c1..fde331696fdd 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h @@ -22,7 +22,20 @@ struct fbnic_fw_mbx { #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_VERSION 1 #define FBNIC_FW_LOG_MAX_SIZE 256 +/* + * The max amount of logs which can fit in a single mailbox message. Firmware + * assumes each mailbox message is 4096B. The amount of messages supported is + * calculated as 4096 minus headers for message, arrays, and length minus the + * size of length divided by headers for each array plus the maximum LOG size, + * and the size of MSEC and INDEX. Put another way: + * + * MAX_LOG_HISTORY = ((4096 - TLV_HDR_SZ * 5 - LENGTH_SZ) + * / (FBNIC_FW_LOG_MAX_SIZE + TLV_HDR_SZ * 3 + MSEC_SZ + * + INDEX_SZ)) + */ +#define FBNIC_FW_MAX_LOG_HISTORY 14 struct fbnic_fw_ver { u32 version; @@ -82,6 +95,8 @@ int fbnic_fw_xmit_fw_write_chunk(struct fbnic_dev *fbd, int cancel_error); int fbnic_fw_xmit_tsene_read_msg(struct fbnic_dev *fbd, struct fbnic_fw_completion *cmpl_data); +int fbnic_fw_xmit_send_logs(struct fbnic_dev *fbd, bool enable, + bool send_log_history); struct fbnic_fw_completion *fbnic_fw_alloc_cmpl(u32 msg_type); void fbnic_fw_put_cmpl(struct fbnic_fw_completion *cmpl_data); @@ -125,6 +140,9 @@ enum { FBNIC_TLV_MSG_ID_FW_FINISH_UPGRADE_RESP = 0x29, FBNIC_TLV_MSG_ID_TSENE_READ_REQ = 0x3C, FBNIC_TLV_MSG_ID_TSENE_READ_RESP = 0x3D, + FBNIC_TLV_MSG_ID_LOG_SEND_LOGS_REQ = 0x43, + FBNIC_TLV_MSG_ID_LOG_MSG_REQ = 0x44, + FBNIC_TLV_MSG_ID_LOG_MSG_RESP = 0x45, }; #define FBNIC_FW_CAP_RESP_VERSION_MAJOR CSR_GENMASK(31, 24) @@ -199,4 +217,22 @@ enum { FBNIC_FW_FINISH_UPGRADE_MSG_MAX }; +enum { + FBNIC_SEND_LOGS = 0x0, + FBNIC_SEND_LOGS_VERSION = 0x1, + FBNIC_SEND_LOGS_HISTORY = 0x2, + FBNIC_SEND_LOGS_MSG_MAX +}; + +enum { + FBNIC_FW_LOG_MSEC = 0x0, + FBNIC_FW_LOG_INDEX = 0x1, + FBNIC_FW_LOG_MSG = 0x2, + FBNIC_FW_LOG_LENGTH = 0x3, + FBNIC_FW_LOG_MSEC_ARRAY = 0x4, + FBNIC_FW_LOG_INDEX_ARRAY = 0x5, + FBNIC_FW_LOG_MSG_ARRAY = 0x6, + FBNIC_FW_LOG_MSG_MAX +}; + #endif /* _FBNIC_FW_H_ */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c new file mode 100644 index 000000000000..38749d47cee6 --- /dev/null +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) Meta Platforms, Inc. and affiliates. */ + +#include <linux/spinlock.h> +#include <linux/vmalloc.h> + +#include "fbnic.h" +#include "fbnic_fw.h" +#include "fbnic_fw_log.h" + +void fbnic_fw_log_enable(struct fbnic_dev *fbd, bool send_hist) +{ + int err; + + if (!fbnic_fw_log_ready(fbd)) + return; + + if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE_HIST) + send_hist = false; + + err = fbnic_fw_xmit_send_logs(fbd, true, send_hist); + if (err && err != -EOPNOTSUPP) + dev_warn(fbd->dev, "Unable to enable firmware logs: %d\n", err); +} + +void fbnic_fw_log_disable(struct fbnic_dev *fbd) +{ + int err; + + err = fbnic_fw_xmit_send_logs(fbd, false, false); + if (err && err != -EOPNOTSUPP) + dev_warn(fbd->dev, "Unable to disable firmware logs: %d\n", + err); +} + +int fbnic_fw_log_init(struct fbnic_dev *fbd) +{ + struct fbnic_fw_log *log = &fbd->fw_log; + void *data; + + if (WARN_ON_ONCE(fbnic_fw_log_ready(fbd))) + return -EEXIST; + + data = vmalloc(FBNIC_FW_LOG_SIZE); + if (!data) + return -ENOMEM; + + spin_lock_init(&fbd->fw_log.lock); + INIT_LIST_HEAD(&log->entries); + log->size = FBNIC_FW_LOG_SIZE; + log->data_start = data; + log->data_end = data + FBNIC_FW_LOG_SIZE; + + fbnic_fw_log_enable(fbd, true); + + return 0; +} + +void fbnic_fw_log_free(struct fbnic_dev *fbd) +{ + struct fbnic_fw_log *log = &fbd->fw_log; + + if (!fbnic_fw_log_ready(fbd)) + return; + + fbnic_fw_log_disable(fbd); + INIT_LIST_HEAD(&log->entries); + log->size = 0; + vfree(log->data_start); + log->data_start = NULL; + log->data_end = NULL; +} + +int fbnic_fw_log_write(struct fbnic_dev *fbd, u64 index, u32 timestamp, + char *msg) +{ + struct fbnic_fw_log_entry *entry, *head, *tail, *next; + struct fbnic_fw_log *log = &fbd->fw_log; + size_t msg_len = strlen(msg) + 1; + unsigned long flags; + void *entry_end; + + if (!fbnic_fw_log_ready(fbd)) { + dev_err(fbd->dev, "Firmware sent log entry without being requested!\n"); + return -ENOSPC; + } + + spin_lock_irqsave(&log->lock, flags); + + if (list_empty(&log->entries)) { + entry = log->data_start; + } else { + head = list_first_entry(&log->entries, typeof(*head), list); + entry = (struct fbnic_fw_log_entry *)&head->msg[head->len + 1]; + entry = PTR_ALIGN(entry, 8); + } + + entry_end = &entry->msg[msg_len + 1]; + + /* We've reached the end of the buffer, wrap around */ + if (entry_end > log->data_end) { + entry = log->data_start; + entry_end = &entry->msg[msg_len + 1]; + } + + /* Make room for entry by removing from tail. */ + list_for_each_entry_safe_reverse(tail, next, &log->entries, list) { + if (entry <= tail && entry_end > (void *)tail) + list_del(&tail->list); + else + break; + } + + entry->index = index; + entry->timestamp = timestamp; + entry->len = msg_len; + strscpy(entry->msg, msg, entry->len); + list_add(&entry->list, &log->entries); + + spin_unlock_irqrestore(&log->lock, flags); + + return 0; +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.h b/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.h new file mode 100644 index 000000000000..cb6555f40a24 --- /dev/null +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) Meta Platforms, Inc. and affiliates. */ + +#ifndef _FBNIC_FW_LOG_H_ +#define _FBNIC_FW_LOG_H_ + +#include <linux/spinlock.h> +#include <linux/types.h> + +/* A 512K log buffer was chosen fairly arbitrarily */ +#define FBNIC_FW_LOG_SIZE (512 * 1024) /* bytes */ + +/* Firmware log output is prepended with log index followed by a timestamp. + * The timestamp is similar to Zephyr's format DD:HH:MM:SS.MMM + */ +#define FBNIC_FW_LOG_FMT "[%5lld] [%02ld:%02ld:%02ld:%02ld.%03ld] %s\n" + +struct fbnic_dev; + +struct fbnic_fw_log_entry { + struct list_head list; + u64 index; + u32 timestamp; + u16 len; + char msg[] __counted_by(len); +}; + +struct fbnic_fw_log { + void *data_start; + void *data_end; + size_t size; + struct list_head entries; + /* Spin lock for accessing or modifying entries */ + spinlock_t lock; +}; + +#define fbnic_fw_log_ready(_fbd) (!!(_fbd)->fw_log.data_start) + +void fbnic_fw_log_enable(struct fbnic_dev *fbd, bool send_hist); +void fbnic_fw_log_disable(struct fbnic_dev *fbd); +int fbnic_fw_log_init(struct fbnic_dev *fbd); +void fbnic_fw_log_free(struct fbnic_dev *fbd); +int fbnic_fw_log_write(struct fbnic_dev *fbd, u64 index, u32 timestamp, + char *msg); +#endif /* _FBNIC_FW_LOG_H_ */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c index 249d3ef862d5..b70e4cadb37b 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c @@ -291,6 +291,17 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto free_irqs; } + /* Send the request to enable the FW logging to host. Note if this + * fails we ignore the error and just display a message as it is + * possible the FW is just too old to support the logging and needs + * to be updated. + */ + err = fbnic_fw_log_init(fbd); + if (err) + dev_warn(fbd->dev, + "Unable to initialize firmware log buffer: %d\n", + err); + fbnic_devlink_register(fbd); fbnic_dbg_fbd_init(fbd); spin_lock_init(&fbd->hw_stats_lock); @@ -365,6 +376,7 @@ static void fbnic_remove(struct pci_dev *pdev) fbnic_hwmon_unregister(fbd); fbnic_dbg_fbd_exit(fbd); fbnic_devlink_unregister(fbd); + fbnic_fw_log_free(fbd); fbnic_fw_free_mbx(fbd); fbnic_free_irqs(fbd); @@ -389,6 +401,8 @@ static int fbnic_pm_suspend(struct device *dev) rtnl_unlock(); null_uc_addr: + fbnic_fw_log_disable(fbd); + devl_lock(priv_to_devlink(fbd)); fbnic_fw_free_mbx(fbd); @@ -434,6 +448,11 @@ static int __fbnic_pm_resume(struct device *dev) devl_unlock(priv_to_devlink(fbd)); + /* Only send log history if log buffer is empty to prevent duplicate + * log entries. + */ + fbnic_fw_log_enable(fbd, list_empty(&fbd->fw_log.entries)); + /* No netdev means there isn't a network interface to bring up */ if (fbnic_init_failure(fbd)) return 0; @@ -455,6 +474,8 @@ static int __fbnic_pm_resume(struct device *dev) return 0; err_free_mbx: + fbnic_fw_log_disable(fbd); + rtnl_unlock(); fbnic_fw_free_mbx(fbd); err_free_irqs: |