[Intel-wired-lan] [PATCH net-next v9 5/5] ice: add ability to enable FW logging and capture data
Paul M Stillwell Jr
paul.m.stillwell.jr at intel.com
Thu Mar 2 21:51:09 UTC 2023
Once the FW logging is configured then users need to be able to enable
logging and capture the data. To enable/disable FW logging use 'enable
fwlog' and 'disable fwlog'. An example command to enable FW logging:
echo enable fwlog > /sys/kernel/debug/ice/0000\:18\:00.0/fwlog
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 the FW
team for debugging.
Signed-off-by: Paul M Stillwell Jr <paul.m.stillwell.jr at intel.com>
---
drivers/net/ethernet/intel/ice/ice.h | 11 +++
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 3 +
drivers/net/ethernet/intel/ice/ice_debugfs.c | 50 +++++++++++
drivers/net/ethernet/intel/ice/ice_main.c | 89 +++++++++++++++++++
4 files changed, 153 insertions(+)
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index f57ad5526b19..9d2bc06ed529 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 a611e0d98bd0..68ab19678dbe 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -2075,7 +2075,9 @@ 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;
@@ -2375,6 +2377,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 cead1c71ff50..7952cfafaa66 100644
--- a/drivers/net/ethernet/intel/ice/ice_debugfs.c
+++ b/drivers/net/ethernet/intel/ice/ice_debugfs.c
@@ -10,6 +10,55 @@
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) {
@@ -289,6 +338,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 3610c56748b7..92ff1292f8fe 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,7 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
hw->debug_mask = debug;
#endif
+ ice_pf_fwlog_init(pf);
ice_debugfs_fwlog_init(pf);
err = ice_init(pf);
@@ -5362,6 +5450,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