Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'eth-fbnic-add-firmware-logging-support'

Lee Trager says:

====================
eth: fbnic: Add firmware logging support

Firmware running on fbnic generates device logs. These logs contain useful
information about the device which may or may not be related to the host.
Logs are stored in a ring buffer and accessible through DebugFS.
====================

Link: https://patch.msgid.link/20250702192207.697368-1-lee@trager.us
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+451 -13
+1
drivers/net/ethernet/meta/fbnic/Makefile
··· 12 12 fbnic_devlink.o \ 13 13 fbnic_ethtool.o \ 14 14 fbnic_fw.o \ 15 + fbnic_fw_log.o \ 15 16 fbnic_hw_stats.o \ 16 17 fbnic_hwmon.o \ 17 18 fbnic_irq.o \
+3
drivers/net/ethernet/meta/fbnic/fbnic.h
··· 12 12 13 13 #include "fbnic_csr.h" 14 14 #include "fbnic_fw.h" 15 + #include "fbnic_fw_log.h" 15 16 #include "fbnic_hw_stats.h" 16 17 #include "fbnic_mac.h" 17 18 #include "fbnic_rpc.h" ··· 86 85 87 86 /* Lock protecting access to hw_stats */ 88 87 spinlock_t hw_stats_lock; 88 + 89 + struct fbnic_fw_log fw_log; 89 90 }; 90 91 91 92 /* Reserve entry 0 in the MSI-X "others" array until we have filled all
+21 -6
drivers/net/ethernet/meta/fbnic/fbnic_csr.h
··· 12 12 #define DESC_BIT(nr) BIT_ULL(nr) 13 13 #define DESC_GENMASK(h, l) GENMASK_ULL(h, l) 14 14 15 + #define FW_VER_CODE(_major, _minor, _patch, _build) ( \ 16 + FIELD_PREP(FBNIC_FW_CAP_RESP_VERSION_MAJOR, _major) | \ 17 + FIELD_PREP(FBNIC_FW_CAP_RESP_VERSION_MINOR, _minor) | \ 18 + FIELD_PREP(FBNIC_FW_CAP_RESP_VERSION_PATCH, _patch) | \ 19 + FIELD_PREP(FBNIC_FW_CAP_RESP_VERSION_BUILD, _build)) 20 + 15 21 /* Defines the minimum firmware version required by the driver */ 16 - #define MIN_FW_MAJOR_VERSION 0 17 - #define MIN_FW_MINOR_VERSION 10 18 - #define MIN_FW_BUILD_VERSION 6 19 - #define MIN_FW_VERSION_CODE (MIN_FW_MAJOR_VERSION * (1u << 24) + \ 20 - MIN_FW_MINOR_VERSION * (1u << 16) + \ 21 - MIN_FW_BUILD_VERSION) 22 + #define MIN_FW_VER_CODE FW_VER_CODE(0, 10, 6, 0) 23 + 24 + /* Defines the minimum firmware version required for firmware logs */ 25 + #define MIN_FW_VER_CODE_LOG FW_VER_CODE(0, 12, 9, 0) 26 + 27 + /* Driver can request that firmware sends all cached logs in bulk. This 28 + * feature was enabled on older firmware however firmware has a bug 29 + * which attempted to send 30 messages per mbx message which caused an 30 + * overflow flooding the mailbox. This results in a kernel warning 31 + * related to corrupt mailbox messages. 32 + * 33 + * If firmware is new enough only request sending historical logs when 34 + * the log buffer is empty to prevent duplicate logs. 35 + */ 36 + #define MIN_FW_VER_CODE_HIST FW_VER_CODE(25, 5, 7, 0) 22 37 23 38 #define PCI_DEVICE_ID_META_FBNIC_ASIC 0x0013 24 39
+29
drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c
··· 170 170 } 171 171 DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_ipo_dst); 172 172 173 + static int fbnic_dbg_fw_log_show(struct seq_file *s, void *v) 174 + { 175 + struct fbnic_dev *fbd = s->private; 176 + struct fbnic_fw_log_entry *entry; 177 + unsigned long flags; 178 + 179 + if (!fbnic_fw_log_ready(fbd)) 180 + return -ENXIO; 181 + 182 + spin_lock_irqsave(&fbd->fw_log.lock, flags); 183 + 184 + list_for_each_entry_reverse(entry, &fbd->fw_log.entries, list) { 185 + seq_printf(s, FBNIC_FW_LOG_FMT, entry->index, 186 + (entry->timestamp / (MSEC_PER_SEC * 60 * 60 * 24)), 187 + (entry->timestamp / (MSEC_PER_SEC * 60 * 60)) % 24, 188 + ((entry->timestamp / (MSEC_PER_SEC * 60) % 60)), 189 + ((entry->timestamp / MSEC_PER_SEC) % 60), 190 + (entry->timestamp % MSEC_PER_SEC), 191 + entry->msg); 192 + } 193 + 194 + spin_unlock_irqrestore(&fbd->fw_log.lock, flags); 195 + 196 + return 0; 197 + } 198 + DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_fw_log); 199 + 173 200 static int fbnic_dbg_pcie_stats_show(struct seq_file *s, void *v) 174 201 { 175 202 struct fbnic_dev *fbd = s->private; ··· 249 222 &fbnic_dbg_ipo_src_fops); 250 223 debugfs_create_file("ipo_dst", 0400, fbd->dbg_fbd, fbd, 251 224 &fbnic_dbg_ipo_dst_fops); 225 + debugfs_create_file("fw_log", 0400, fbd->dbg_fbd, fbd, 226 + &fbnic_dbg_fw_log_fops); 252 227 } 253 228 254 229 void fbnic_dbg_fbd_exit(struct fbnic_dev *fbd)
+172 -7
drivers/net/ethernet/meta/fbnic/fbnic_fw.c
··· 573 573 if (!fbd->fw_cap.running.mgmt.version) 574 574 return -EINVAL; 575 575 576 - if (fbd->fw_cap.running.mgmt.version < MIN_FW_VERSION_CODE) { 576 + if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE) { 577 + char required_ver[FBNIC_FW_VER_MAX_SIZE]; 577 578 char running_ver[FBNIC_FW_VER_MAX_SIZE]; 578 579 579 580 fbnic_mk_fw_ver_str(fbd->fw_cap.running.mgmt.version, 580 581 running_ver); 581 - dev_err(fbd->dev, "Device firmware version(%s) is older than minimum required version(%02d.%02d.%02d)\n", 582 - running_ver, 583 - MIN_FW_MAJOR_VERSION, 584 - MIN_FW_MINOR_VERSION, 585 - MIN_FW_BUILD_VERSION); 582 + fbnic_mk_fw_ver_str(MIN_FW_VER_CODE, required_ver); 583 + dev_err(fbd->dev, "Device firmware version(%s) is older than minimum required version(%s)\n", 584 + running_ver, required_ver); 586 585 /* Disable TX mailbox to prevent card use until firmware is 587 586 * updated. 588 587 */ ··· 1034 1035 return err; 1035 1036 } 1036 1037 1038 + static const struct fbnic_tlv_index fbnic_fw_log_req_index[] = { 1039 + FBNIC_TLV_ATTR_U32(FBNIC_FW_LOG_MSEC), 1040 + FBNIC_TLV_ATTR_U64(FBNIC_FW_LOG_INDEX), 1041 + FBNIC_TLV_ATTR_STRING(FBNIC_FW_LOG_MSG, FBNIC_FW_LOG_MAX_SIZE), 1042 + FBNIC_TLV_ATTR_U32(FBNIC_FW_LOG_LENGTH), 1043 + FBNIC_TLV_ATTR_ARRAY(FBNIC_FW_LOG_MSEC_ARRAY), 1044 + FBNIC_TLV_ATTR_ARRAY(FBNIC_FW_LOG_INDEX_ARRAY), 1045 + FBNIC_TLV_ATTR_ARRAY(FBNIC_FW_LOG_MSG_ARRAY), 1046 + FBNIC_TLV_ATTR_LAST 1047 + }; 1048 + 1049 + static int fbnic_fw_process_log_array(struct fbnic_tlv_msg **results, 1050 + u16 length, u16 arr_type_idx, 1051 + u16 attr_type_idx, 1052 + struct fbnic_tlv_msg **tlv_array_out) 1053 + { 1054 + struct fbnic_tlv_msg *attr; 1055 + int attr_len; 1056 + int err; 1057 + 1058 + if (!results[attr_type_idx]) 1059 + return -EINVAL; 1060 + 1061 + tlv_array_out[0] = results[attr_type_idx]; 1062 + 1063 + if (!length) 1064 + return 0; 1065 + 1066 + if (!results[arr_type_idx]) 1067 + return -EINVAL; 1068 + 1069 + attr = results[arr_type_idx]; 1070 + attr_len = le16_to_cpu(attr->hdr.len) / sizeof(u32) - 1; 1071 + err = fbnic_tlv_attr_parse_array(&attr[1], attr_len, &tlv_array_out[1], 1072 + fbnic_fw_log_req_index, 1073 + attr_type_idx, 1074 + length); 1075 + if (err) 1076 + return err; 1077 + 1078 + return 0; 1079 + } 1080 + 1081 + static int fbnic_fw_parse_logs(struct fbnic_dev *fbd, 1082 + struct fbnic_tlv_msg **msec_tlv, 1083 + struct fbnic_tlv_msg **index_tlv, 1084 + struct fbnic_tlv_msg **log_tlv, 1085 + int count) 1086 + { 1087 + int i; 1088 + 1089 + for (i = 0; i < count; i++) { 1090 + char log[FBNIC_FW_LOG_MAX_SIZE]; 1091 + ssize_t len; 1092 + u64 index; 1093 + u32 msec; 1094 + int err; 1095 + 1096 + if (!msec_tlv[i] || !index_tlv[i] || !log_tlv[i]) { 1097 + dev_warn(fbd->dev, "Received log message with missing attributes!\n"); 1098 + return -EINVAL; 1099 + } 1100 + 1101 + index = fbnic_tlv_attr_get_signed(index_tlv[i], 0); 1102 + msec = fbnic_tlv_attr_get_signed(msec_tlv[i], 0); 1103 + len = fbnic_tlv_attr_get_string(log_tlv[i], log, 1104 + FBNIC_FW_LOG_MAX_SIZE); 1105 + if (len < 0) 1106 + return len; 1107 + 1108 + err = fbnic_fw_log_write(fbd, index, msec, log); 1109 + if (err) 1110 + return err; 1111 + } 1112 + 1113 + return 0; 1114 + } 1115 + 1116 + static int fbnic_fw_parse_log_req(void *opaque, 1117 + struct fbnic_tlv_msg **results) 1118 + { 1119 + struct fbnic_tlv_msg *index_tlv[FBNIC_FW_MAX_LOG_HISTORY]; 1120 + struct fbnic_tlv_msg *msec_tlv[FBNIC_FW_MAX_LOG_HISTORY]; 1121 + struct fbnic_tlv_msg *log_tlv[FBNIC_FW_MAX_LOG_HISTORY]; 1122 + struct fbnic_dev *fbd = opaque; 1123 + u16 length; 1124 + int err; 1125 + 1126 + length = fta_get_uint(results, FBNIC_FW_LOG_LENGTH); 1127 + if (length >= FBNIC_FW_MAX_LOG_HISTORY) 1128 + return -E2BIG; 1129 + 1130 + err = fbnic_fw_process_log_array(results, length, 1131 + FBNIC_FW_LOG_MSEC_ARRAY, 1132 + FBNIC_FW_LOG_MSEC, msec_tlv); 1133 + if (err) 1134 + return err; 1135 + 1136 + err = fbnic_fw_process_log_array(results, length, 1137 + FBNIC_FW_LOG_INDEX_ARRAY, 1138 + FBNIC_FW_LOG_INDEX, index_tlv); 1139 + if (err) 1140 + return err; 1141 + 1142 + err = fbnic_fw_process_log_array(results, length, 1143 + FBNIC_FW_LOG_MSG_ARRAY, 1144 + FBNIC_FW_LOG_MSG, log_tlv); 1145 + if (err) 1146 + return err; 1147 + 1148 + err = fbnic_fw_parse_logs(fbd, msec_tlv, index_tlv, log_tlv, 1149 + length + 1); 1150 + if (err) 1151 + return err; 1152 + 1153 + return 0; 1154 + } 1155 + 1156 + int fbnic_fw_xmit_send_logs(struct fbnic_dev *fbd, bool enable, 1157 + bool send_log_history) 1158 + { 1159 + struct fbnic_tlv_msg *msg; 1160 + int err; 1161 + 1162 + if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE_LOG) { 1163 + dev_warn(fbd->dev, "Firmware version is too old to support firmware logs!\n"); 1164 + return -EOPNOTSUPP; 1165 + } 1166 + 1167 + msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_LOG_SEND_LOGS_REQ); 1168 + if (!msg) 1169 + return -ENOMEM; 1170 + 1171 + if (enable) { 1172 + err = fbnic_tlv_attr_put_flag(msg, FBNIC_SEND_LOGS); 1173 + if (err) 1174 + goto free_message; 1175 + 1176 + /* Report request for version 1 of logs */ 1177 + err = fbnic_tlv_attr_put_int(msg, FBNIC_SEND_LOGS_VERSION, 1178 + FBNIC_FW_LOG_VERSION); 1179 + if (err) 1180 + goto free_message; 1181 + 1182 + if (send_log_history) { 1183 + err = fbnic_tlv_attr_put_flag(msg, 1184 + FBNIC_SEND_LOGS_HISTORY); 1185 + if (err) 1186 + goto free_message; 1187 + } 1188 + } 1189 + 1190 + err = fbnic_mbx_map_tlv_msg(fbd, msg); 1191 + if (err) 1192 + goto free_message; 1193 + 1194 + return 0; 1195 + 1196 + free_message: 1197 + free_page((unsigned long)msg); 1198 + return err; 1199 + } 1200 + 1037 1201 static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = { 1038 1202 FBNIC_TLV_PARSER(FW_CAP_RESP, fbnic_fw_cap_resp_index, 1039 1203 fbnic_fw_parse_cap_resp), ··· 1216 1054 FBNIC_TLV_PARSER(TSENE_READ_RESP, 1217 1055 fbnic_tsene_read_resp_index, 1218 1056 fbnic_fw_parse_tsene_read_resp), 1057 + FBNIC_TLV_PARSER(LOG_MSG_REQ, 1058 + fbnic_fw_log_req_index, 1059 + fbnic_fw_parse_log_req), 1219 1060 FBNIC_TLV_MSG_ERROR 1220 1061 }; 1221 1062 ··· 1332 1167 * to indicate we entered the polling state waiting for a response 1333 1168 */ 1334 1169 for (fbd->fw_cap.running.mgmt.version = 1; 1335 - fbd->fw_cap.running.mgmt.version < MIN_FW_VERSION_CODE;) { 1170 + fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE;) { 1336 1171 if (!tx_mbx->ready) 1337 1172 err = -ENODEV; 1338 1173 if (err)
+36
drivers/net/ethernet/meta/fbnic/fbnic_fw.h
··· 22 22 #define FBNIC_FW_VER_MAX_SIZE 32 23 23 // Formatted version is in the format XX.YY.ZZ_RRR_COMMIT 24 24 #define FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE (FBNIC_FW_VER_MAX_SIZE - 13) 25 + #define FBNIC_FW_LOG_VERSION 1 25 26 #define FBNIC_FW_LOG_MAX_SIZE 256 27 + /* 28 + * The max amount of logs which can fit in a single mailbox message. Firmware 29 + * assumes each mailbox message is 4096B. The amount of messages supported is 30 + * calculated as 4096 minus headers for message, arrays, and length minus the 31 + * size of length divided by headers for each array plus the maximum LOG size, 32 + * and the size of MSEC and INDEX. Put another way: 33 + * 34 + * MAX_LOG_HISTORY = ((4096 - TLV_HDR_SZ * 5 - LENGTH_SZ) 35 + * / (FBNIC_FW_LOG_MAX_SIZE + TLV_HDR_SZ * 3 + MSEC_SZ 36 + * + INDEX_SZ)) 37 + */ 38 + #define FBNIC_FW_MAX_LOG_HISTORY 14 26 39 27 40 struct fbnic_fw_ver { 28 41 u32 version; ··· 95 82 int cancel_error); 96 83 int fbnic_fw_xmit_tsene_read_msg(struct fbnic_dev *fbd, 97 84 struct fbnic_fw_completion *cmpl_data); 85 + int fbnic_fw_xmit_send_logs(struct fbnic_dev *fbd, bool enable, 86 + bool send_log_history); 98 87 struct fbnic_fw_completion *fbnic_fw_alloc_cmpl(u32 msg_type); 99 88 void fbnic_fw_put_cmpl(struct fbnic_fw_completion *cmpl_data); 100 89 ··· 140 125 FBNIC_TLV_MSG_ID_FW_FINISH_UPGRADE_RESP = 0x29, 141 126 FBNIC_TLV_MSG_ID_TSENE_READ_REQ = 0x3C, 142 127 FBNIC_TLV_MSG_ID_TSENE_READ_RESP = 0x3D, 128 + FBNIC_TLV_MSG_ID_LOG_SEND_LOGS_REQ = 0x43, 129 + FBNIC_TLV_MSG_ID_LOG_MSG_REQ = 0x44, 130 + FBNIC_TLV_MSG_ID_LOG_MSG_RESP = 0x45, 143 131 }; 144 132 145 133 #define FBNIC_FW_CAP_RESP_VERSION_MAJOR CSR_GENMASK(31, 24) ··· 215 197 enum { 216 198 FBNIC_FW_FINISH_UPGRADE_ERROR = 0x0, 217 199 FBNIC_FW_FINISH_UPGRADE_MSG_MAX 200 + }; 201 + 202 + enum { 203 + FBNIC_SEND_LOGS = 0x0, 204 + FBNIC_SEND_LOGS_VERSION = 0x1, 205 + FBNIC_SEND_LOGS_HISTORY = 0x2, 206 + FBNIC_SEND_LOGS_MSG_MAX 207 + }; 208 + 209 + enum { 210 + FBNIC_FW_LOG_MSEC = 0x0, 211 + FBNIC_FW_LOG_INDEX = 0x1, 212 + FBNIC_FW_LOG_MSG = 0x2, 213 + FBNIC_FW_LOG_LENGTH = 0x3, 214 + FBNIC_FW_LOG_MSEC_ARRAY = 0x4, 215 + FBNIC_FW_LOG_INDEX_ARRAY = 0x5, 216 + FBNIC_FW_LOG_MSG_ARRAY = 0x6, 217 + FBNIC_FW_LOG_MSG_MAX 218 218 }; 219 219 220 220 #endif /* _FBNIC_FW_H_ */
+123
drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) Meta Platforms, Inc. and affiliates. */ 3 + 4 + #include <linux/spinlock.h> 5 + #include <linux/vmalloc.h> 6 + 7 + #include "fbnic.h" 8 + #include "fbnic_fw.h" 9 + #include "fbnic_fw_log.h" 10 + 11 + void fbnic_fw_log_enable(struct fbnic_dev *fbd, bool send_hist) 12 + { 13 + int err; 14 + 15 + if (!fbnic_fw_log_ready(fbd)) 16 + return; 17 + 18 + if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE_HIST) 19 + send_hist = false; 20 + 21 + err = fbnic_fw_xmit_send_logs(fbd, true, send_hist); 22 + if (err && err != -EOPNOTSUPP) 23 + dev_warn(fbd->dev, "Unable to enable firmware logs: %d\n", err); 24 + } 25 + 26 + void fbnic_fw_log_disable(struct fbnic_dev *fbd) 27 + { 28 + int err; 29 + 30 + err = fbnic_fw_xmit_send_logs(fbd, false, false); 31 + if (err && err != -EOPNOTSUPP) 32 + dev_warn(fbd->dev, "Unable to disable firmware logs: %d\n", 33 + err); 34 + } 35 + 36 + int fbnic_fw_log_init(struct fbnic_dev *fbd) 37 + { 38 + struct fbnic_fw_log *log = &fbd->fw_log; 39 + void *data; 40 + 41 + if (WARN_ON_ONCE(fbnic_fw_log_ready(fbd))) 42 + return -EEXIST; 43 + 44 + data = vmalloc(FBNIC_FW_LOG_SIZE); 45 + if (!data) 46 + return -ENOMEM; 47 + 48 + spin_lock_init(&fbd->fw_log.lock); 49 + INIT_LIST_HEAD(&log->entries); 50 + log->size = FBNIC_FW_LOG_SIZE; 51 + log->data_start = data; 52 + log->data_end = data + FBNIC_FW_LOG_SIZE; 53 + 54 + fbnic_fw_log_enable(fbd, true); 55 + 56 + return 0; 57 + } 58 + 59 + void fbnic_fw_log_free(struct fbnic_dev *fbd) 60 + { 61 + struct fbnic_fw_log *log = &fbd->fw_log; 62 + 63 + if (!fbnic_fw_log_ready(fbd)) 64 + return; 65 + 66 + fbnic_fw_log_disable(fbd); 67 + INIT_LIST_HEAD(&log->entries); 68 + log->size = 0; 69 + vfree(log->data_start); 70 + log->data_start = NULL; 71 + log->data_end = NULL; 72 + } 73 + 74 + int fbnic_fw_log_write(struct fbnic_dev *fbd, u64 index, u32 timestamp, 75 + char *msg) 76 + { 77 + struct fbnic_fw_log_entry *entry, *head, *tail, *next; 78 + struct fbnic_fw_log *log = &fbd->fw_log; 79 + size_t msg_len = strlen(msg) + 1; 80 + unsigned long flags; 81 + void *entry_end; 82 + 83 + if (!fbnic_fw_log_ready(fbd)) { 84 + dev_err(fbd->dev, "Firmware sent log entry without being requested!\n"); 85 + return -ENOSPC; 86 + } 87 + 88 + spin_lock_irqsave(&log->lock, flags); 89 + 90 + if (list_empty(&log->entries)) { 91 + entry = log->data_start; 92 + } else { 93 + head = list_first_entry(&log->entries, typeof(*head), list); 94 + entry = (struct fbnic_fw_log_entry *)&head->msg[head->len + 1]; 95 + entry = PTR_ALIGN(entry, 8); 96 + } 97 + 98 + entry_end = &entry->msg[msg_len + 1]; 99 + 100 + /* We've reached the end of the buffer, wrap around */ 101 + if (entry_end > log->data_end) { 102 + entry = log->data_start; 103 + entry_end = &entry->msg[msg_len + 1]; 104 + } 105 + 106 + /* Make room for entry by removing from tail. */ 107 + list_for_each_entry_safe_reverse(tail, next, &log->entries, list) { 108 + if (entry <= tail && entry_end > (void *)tail) 109 + list_del(&tail->list); 110 + else 111 + break; 112 + } 113 + 114 + entry->index = index; 115 + entry->timestamp = timestamp; 116 + entry->len = msg_len; 117 + strscpy(entry->msg, msg, entry->len); 118 + list_add(&entry->list, &log->entries); 119 + 120 + spin_unlock_irqrestore(&log->lock, flags); 121 + 122 + return 0; 123 + }
+45
drivers/net/ethernet/meta/fbnic/fbnic_fw_log.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (c) Meta Platforms, Inc. and affiliates. */ 3 + 4 + #ifndef _FBNIC_FW_LOG_H_ 5 + #define _FBNIC_FW_LOG_H_ 6 + 7 + #include <linux/spinlock.h> 8 + #include <linux/types.h> 9 + 10 + /* A 512K log buffer was chosen fairly arbitrarily */ 11 + #define FBNIC_FW_LOG_SIZE (512 * 1024) /* bytes */ 12 + 13 + /* Firmware log output is prepended with log index followed by a timestamp. 14 + * The timestamp is similar to Zephyr's format DD:HH:MM:SS.MMM 15 + */ 16 + #define FBNIC_FW_LOG_FMT "[%5lld] [%02ld:%02ld:%02ld:%02ld.%03ld] %s\n" 17 + 18 + struct fbnic_dev; 19 + 20 + struct fbnic_fw_log_entry { 21 + struct list_head list; 22 + u64 index; 23 + u32 timestamp; 24 + u16 len; 25 + char msg[] __counted_by(len); 26 + }; 27 + 28 + struct fbnic_fw_log { 29 + void *data_start; 30 + void *data_end; 31 + size_t size; 32 + struct list_head entries; 33 + /* Spin lock for accessing or modifying entries */ 34 + spinlock_t lock; 35 + }; 36 + 37 + #define fbnic_fw_log_ready(_fbd) (!!(_fbd)->fw_log.data_start) 38 + 39 + void fbnic_fw_log_enable(struct fbnic_dev *fbd, bool send_hist); 40 + void fbnic_fw_log_disable(struct fbnic_dev *fbd); 41 + int fbnic_fw_log_init(struct fbnic_dev *fbd); 42 + void fbnic_fw_log_free(struct fbnic_dev *fbd); 43 + int fbnic_fw_log_write(struct fbnic_dev *fbd, u64 index, u32 timestamp, 44 + char *msg); 45 + #endif /* _FBNIC_FW_LOG_H_ */
+21
drivers/net/ethernet/meta/fbnic/fbnic_pci.c
··· 291 291 goto free_irqs; 292 292 } 293 293 294 + /* Send the request to enable the FW logging to host. Note if this 295 + * fails we ignore the error and just display a message as it is 296 + * possible the FW is just too old to support the logging and needs 297 + * to be updated. 298 + */ 299 + err = fbnic_fw_log_init(fbd); 300 + if (err) 301 + dev_warn(fbd->dev, 302 + "Unable to initialize firmware log buffer: %d\n", 303 + err); 304 + 294 305 fbnic_devlink_register(fbd); 295 306 fbnic_dbg_fbd_init(fbd); 296 307 spin_lock_init(&fbd->hw_stats_lock); ··· 376 365 fbnic_hwmon_unregister(fbd); 377 366 fbnic_dbg_fbd_exit(fbd); 378 367 fbnic_devlink_unregister(fbd); 368 + fbnic_fw_log_free(fbd); 379 369 fbnic_fw_free_mbx(fbd); 380 370 fbnic_free_irqs(fbd); 381 371 ··· 401 389 rtnl_unlock(); 402 390 403 391 null_uc_addr: 392 + fbnic_fw_log_disable(fbd); 393 + 404 394 devl_lock(priv_to_devlink(fbd)); 405 395 406 396 fbnic_fw_free_mbx(fbd); ··· 448 434 449 435 devl_unlock(priv_to_devlink(fbd)); 450 436 437 + /* Only send log history if log buffer is empty to prevent duplicate 438 + * log entries. 439 + */ 440 + fbnic_fw_log_enable(fbd, list_empty(&fbd->fw_log.entries)); 441 + 451 442 /* No netdev means there isn't a network interface to bring up */ 452 443 if (fbnic_init_failure(fbd)) 453 444 return 0; ··· 474 455 475 456 return 0; 476 457 err_free_mbx: 458 + fbnic_fw_log_disable(fbd); 459 + 477 460 rtnl_unlock(); 478 461 fbnic_fw_free_mbx(fbd); 479 462 err_free_irqs: