[Intel-wired-lan] [PATCH net-next v10 5/5] ice: add ability to read FW logging data
Paul M Stillwell Jr
paul.m.stillwell.jr at intel.com
Wed Mar 8 23:51:02 UTC 2023
Once logging is enable the user should read the data using something
like:
cat /sys/kernel/debug/ice/0000\:18\:00.0/fwlog > log_data.bin
The output file is a binary file that will be decoded by Intel for
debugging.
Also added documentation to
Documentation/networking/device-drivers/ethernet/intel/ice.rst
Signed-off-by: Paul M Stillwell Jr <paul.m.stillwell.jr at intel.com>
---
.../device_drivers/ethernet/intel/ice.rst | 85 ++++++++++++++++++
drivers/net/ethernet/intel/ice/ice.h | 11 +++
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 2 +
drivers/net/ethernet/intel/ice/ice_debugfs.c | 51 +++++++++++
drivers/net/ethernet/intel/ice/ice_main.c | 90 +++++++++++++++++++
5 files changed, 239 insertions(+)
diff --git a/Documentation/networking/device_drivers/ethernet/intel/ice.rst b/Documentation/networking/device_drivers/ethernet/intel/ice.rst
index 5efea4dd1251..c59f4f89ee84 100644
--- a/Documentation/networking/device_drivers/ethernet/intel/ice.rst
+++ b/Documentation/networking/device_drivers/ethernet/intel/ice.rst
@@ -913,6 +913,91 @@ driver writes raw bytes by the GNSS object to the receiver through i2c. Please
refer to the hardware GNSS module documentation for configuration details.
+Firmware (FW) logging
+---------------------
+The driver supports FW logging via the debugfs interface on PF 0 only. In order
+for FW logging to work, the NVM must support it. The 'fwlog' file will only get
+created in the ice debugfs directory if the NVM supports FW logging.
+
+To see the status of FW logging the 'dump fwlog_cfg' command can be used::
+
+ # echo dump fwlog_cfg > /sys/kernel/debug/ice/<pci-dev>/fwlog
+
+To configure FW logging the 'update fwlog_cfg' command can be used::
+
+ # update fwlog_cfg <fwlog_level> <fwlog_events> <fwlog_resolution>
+
+where
+
+* fwlog_level is a value from 0-4 as described below. Each level includes the
+ messages from the previous/lower level
+
+ * 0 - no logging
+ * 1 - error logging
+ * 2 - warning logging
+ * 3 - normal logging
+ * 4 - verbose logging
+
+* fwlog_events is a value from 0-32 that represents the module to receive
+ events for. The values are sent as a hex value where each bit represents
+ a specific module. The module values are
+
+ * 0 (0x00) - General
+ * 1 (0x01) - Control (Resets + Autoload)
+ * 2 (0x02) - Link Management
+ * 3 (0x03) - Link Topology Detection
+ * 4 (0x04) - DNL
+ * 5 (0x05) - I2C
+ * 6 (0x06) - SDP
+ * 7 (0x07) - MDIO
+ * 8 (0x08) - Admin Queue
+ * 9 (0x09) - HDMA
+ * 10 (0x0A) - LLDP
+ * 11 (0x0B) - DCBX
+ * 12 (0x0C) - DCB
+ * 13 (0x0D) - XLR
+ * 14 (0x0E) - NVM
+ * 15 (0x0F) - Authentication
+ * 16 (0x10) - VPD
+ * 17 (0x11) - IOSF
+ * 18 (0x12) - Parser
+ * 19 (0x13) - Switch
+ * 20 (0x14) - Scheduler
+ * 21 (0x15) - Tx Queue Management
+ * 22 (0x16) - Unsupported
+ * 23 (0x17) - Post
+ * 24 (0x18) - Watchdog
+ * 25 (0x19) - Task Dispatcher
+ * 26 (0x1A) - Manageability
+ * 27 (0x1B) - Synce
+ * 28 (0x1C) - Health
+ * 29 (0x1D) - Time Sync
+ * 30 (0x1E) - PF Registration
+ * 31 (0x1F) - Module Version
+
+* fwlog_resolution is the number of log messages to included
+ in a single ARQ event. The range is 1-128 (1 means push every log
+ message, 128 means push only when the max AQ command buffer is full).
+ The suggested value is 10.
+
+An example command to enable FW logging is::
+
+ # echo update fwlog_cfg 4 0x82414821 50 > /sys/kernel/debug/ice/<pci-dev>/fwlog
+
+To turn on FW logging the 'enable fwlog' command can be used::
+
+ # echo enable fwlog > /sys/kernel/debug/ice/<pci-dev>/fwlog
+
+To turn off FW logging the 'disable fwlog' command can be used::
+
+ # echo disable fwlog > /sys/kernel/debug/ice/<pci-dev>/fwlog
+
+To read the data captured by the driver the user can read from the 'fwlog'
+file::
+
+ # cat /sys/kernel/debug/ice/<pci-dev>/fwlog > fwlog.bin
+
+
Performance Optimization
========================
Driver defaults are meant to fit a wide variety of workloads, but if further
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 4b90955b1308..c7c47aa90397 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -636,6 +636,8 @@ struct ice_pf {
#define ICE_VF_AGG_NODE_ID_START 65
#define ICE_MAX_VF_AGG_NODES 32
struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
+ struct list_head fwlog_data_list;
+ u8 fwlog_list_count;
};
struct ice_netdev_priv {
@@ -650,6 +652,15 @@ struct ice_netdev_priv {
struct list_head tc_indr_block_priv_list;
};
+struct ice_fwlog_data {
+ struct list_head list;
+ u16 data_size;
+ u8 *data;
+};
+
+/* define the maximum number of items that can be in the list */
+#define ICE_FWLOG_MAX_SIZE 128
+
/**
* ice_vector_ch_enabled
* @qv: pointer to q_vector, can be NULL
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 1af036beeb45..56fcba403dec 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -2077,6 +2077,7 @@ enum ice_aqc_fw_logging_mod {
/* Set FW Logging configuration (indirect 0xFF30)
* Register for FW Logging (indirect 0xFF31)
* Query FW Logging (indirect 0xFF32)
+ * FW Log Event (indirect 0xFF33)
*/
struct ice_aqc_fw_log {
u8 cmd_flags;
@@ -2377,6 +2378,7 @@ enum ice_adminq_opc {
ice_aqc_opc_fw_logs_config = 0xFF30,
ice_aqc_opc_fw_logs_register = 0xFF31,
ice_aqc_opc_fw_logs_query = 0xFF32,
+ ice_aqc_opc_fw_logs_event = 0xFF33,
};
#endif /* _ICE_ADMINQ_CMD_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_debugfs.c b/drivers/net/ethernet/intel/ice/ice_debugfs.c
index 62ff065798fc..b25ec0202195 100644
--- a/drivers/net/ethernet/intel/ice/ice_debugfs.c
+++ b/drivers/net/ethernet/intel/ice/ice_debugfs.c
@@ -4,10 +4,60 @@
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/random.h>
+#include <linux/vmalloc.h>
#include "ice.h"
static struct dentry *ice_debugfs_root;
+/**
+ * ice_debugfs_command_read - read from command datum
+ * @filp: the opened file
+ * @buffer: where to write the data for the user to read
+ * @count: the size of the user's buffer
+ * @ppos: file position offset
+ */
+static ssize_t ice_debugfs_command_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct ice_pf *pf = filp->private_data;
+ struct ice_fwlog_data *log, *tmp_log;
+ int data_copied = 0;
+ int copy_count = 0;
+
+ if (list_empty(&pf->fwlog_data_list))
+ return 0;
+
+ list_for_each_entry(log, &pf->fwlog_data_list, list) {
+ u16 cur_buf_len = log->data_size;
+
+ if (cur_buf_len >= count)
+ break;
+
+ if (copy_to_user(buffer, log->data, cur_buf_len))
+ return -EFAULT;
+
+ data_copied += cur_buf_len;
+ buffer += cur_buf_len;
+ count -= cur_buf_len;
+ *ppos += cur_buf_len;
+ copy_count++;
+ }
+
+ /* only free the data once we know there weren't any errors */
+ list_for_each_entry_safe(log, tmp_log, &pf->fwlog_data_list, list) {
+ if (!copy_count)
+ break;
+
+ vfree(log->data);
+ list_del(&log->list);
+ vfree(log);
+ pf->fwlog_list_count--;
+ copy_count--;
+ }
+
+ return data_copied;
+}
+
static const char *module_id_to_name(u16 module_id)
{
switch (module_id) {
@@ -287,6 +337,7 @@ ice_debugfs_command_write(struct file *filp, const char __user *buf,
static const struct file_operations ice_debugfs_command_fops = {
.owner = THIS_MODULE,
.open = simple_open,
+ .read = ice_debugfs_command_read,
.write = ice_debugfs_command_write,
};
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index ee13518e3c65..5bb924f32479 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -5,6 +5,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/vmalloc.h>
#include <generated/utsrelease.h>
#include "ice.h"
#include "ice_base.h"
@@ -1239,6 +1240,43 @@ ice_handle_link_event(struct ice_pf *pf, struct ice_rq_event_info *event)
return status;
}
+/**
+ * ice_get_fwlog_data - copy the FW log data from ARQ event
+ * @pf: PF that the FW log event is associated with
+ * @event: event structure containing FW log data
+ */
+static void
+ice_get_fwlog_data(struct ice_pf *pf, struct ice_rq_event_info *event)
+{
+ struct device *dev = ice_pf_to_dev(pf);
+ struct ice_fwlog_data *fwlog;
+
+ if (pf->fwlog_list_count >= ICE_FWLOG_MAX_SIZE)
+ return;
+
+ fwlog = vmalloc(sizeof(*fwlog));
+ if (!fwlog) {
+ dev_warn(dev, "Couldn't allocate memory for FWlog element\n");
+ return;
+ }
+
+ INIT_LIST_HEAD(&fwlog->list);
+
+ fwlog->data_size = le16_to_cpu(event->desc.datalen);
+ fwlog->data = vzalloc(fwlog->data_size);
+ if (!fwlog->data) {
+ dev_warn(dev, "Couldn't allocate memory for FWlog data\n");
+ vfree(fwlog);
+ return;
+ }
+
+ memcpy(fwlog->data, event->msg_buf, fwlog->data_size);
+
+ list_add_tail(&fwlog->list, &pf->fwlog_data_list);
+
+ pf->fwlog_list_count++;
+}
+
enum ice_aq_task_state {
ICE_AQ_TASK_WAITING = 0,
ICE_AQ_TASK_COMPLETE,
@@ -1519,6 +1557,9 @@ static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type)
ice_vc_process_vf_msg(pf, &event, &data);
break;
+ case ice_aqc_opc_fw_logs_event:
+ ice_get_fwlog_data(pf, &event);
+ break;
case ice_aqc_opc_lldp_set_mib_change:
ice_dcb_process_lldp_set_mib_change(pf, &event);
break;
@@ -4746,6 +4787,52 @@ static void ice_deinit_eth(struct ice_pf *pf)
ice_decfg_netdev(vsi);
}
+/**
+ * ice_pf_fwlog_init - initialize FW logging on device init
+ * @pf: pointer to the PF struct
+ */
+static void ice_pf_fwlog_init(struct ice_pf *pf)
+{
+ /* only supported on PF 0 */
+ if (pf->hw.bus.func)
+ return;
+
+ INIT_LIST_HEAD(&pf->fwlog_data_list);
+}
+
+/**
+ * ice_pf_fwlog_deinit - clear FW logging metadata on device exit
+ * @pf: pointer to the PF struct
+ */
+static void ice_pf_fwlog_deinit(struct ice_pf *pf)
+{
+ struct ice_fwlog_data *fwlog, *fwlog_tmp;
+ struct ice_hw *hw = &pf->hw;
+ int err;
+
+ /* only supported on PF 0 */
+ if (pf->hw.bus.func)
+ return;
+
+ /* make sure FW logging is disabled to not put the FW in a weird state
+ * for the next driver load
+ */
+ hw->fwlog_cfg.options &= ~ICE_FWLOG_OPTION_ARQ_ENA;
+ err = ice_fwlog_set(hw, &hw->fwlog_cfg);
+ if (err)
+ dev_warn(ice_pf_to_dev(pf), "Unable to turn off FW logging, status: %d\n",
+ err);
+
+ err = ice_fwlog_unregister(hw);
+ if (err)
+ dev_warn(ice_pf_to_dev(pf), "Unable to unregister FW logging, status: %d\n",
+ err);
+
+ list_for_each_entry_safe(fwlog, fwlog_tmp, &pf->fwlog_data_list, list) {
+ vfree(fwlog->data);
+ vfree(fwlog);
+ }
+}
static int ice_init_dev(struct ice_pf *pf)
{
struct device *dev = ice_pf_to_dev(pf);
@@ -5253,6 +5340,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
hw->debug_mask = debug;
#endif
+ ice_pf_fwlog_init(pf);
+
err = ice_init(pf);
if (err)
goto err_init;
@@ -5360,6 +5449,7 @@ static void ice_remove(struct pci_dev *pdev)
msleep(100);
}
+ ice_pf_fwlog_deinit(pf);
ice_debugfs_exit();
if (test_bit(ICE_FLAG_SRIOV_ENA, pf->flags)) {
--
2.35.1
More information about the Intel-wired-lan
mailing list