mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 00:22:00 +02:00
Merge branch '10GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue
Tony Nguyen says: ==================== Fwlog support in ixgbe Michal Swiatkowski says: Firmware logging is a feature that allow user to dump firmware log using debugfs interface. It is supported on device that can handle specific firmware ops. It is true for ice and ixgbe driver. Prepare code from ice driver to be moved to the library code and reuse it in ixgbe driver. * '10GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue: ixgbe: fwlog support for e610 ice, libie: move fwlog code to libie ice: reregister fwlog after driver reinit ice: prepare for moving file to libie ice: move debugfs code to fwlog libie, ice: move fwlog admin queue to libie ice: drop driver specific structure from fwlog code ice: check for PF number outside the fwlog code ice: move out debugfs init from fwlog ice: allow calling custom send function in fwlog ice: add pdev into fwlog structure and use it for logging ice: introduce ice_fwlog structure ice: drop ice_pf_fwlog_update_module() ice: move get_fwlog_data() to fwlog file ice: make fwlog functions static ==================== Link: https://patch.msgid.link/20250911210525.345110-1-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
c20302dd42
|
|
@ -146,6 +146,7 @@ config IXGBE
|
|||
tristate "Intel(R) 10GbE PCI Express adapters support"
|
||||
depends on PCI
|
||||
depends on PTP_1588_CLOCK_OPTIONAL
|
||||
select LIBIE_FWLOG
|
||||
select MDIO
|
||||
select NET_DEVLINK
|
||||
select PLDMFW
|
||||
|
|
@ -297,6 +298,7 @@ config ICE
|
|||
select DIMLIB
|
||||
select LIBIE
|
||||
select LIBIE_ADMINQ
|
||||
select LIBIE_FWLOG
|
||||
select NET_DEVLINK
|
||||
select PACKING
|
||||
select PLDMFW
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ ice-y := ice_main.o \
|
|||
ice_ethtool.o \
|
||||
ice_repr.o \
|
||||
ice_tc_lib.o \
|
||||
ice_fwlog.o \
|
||||
ice_debugfs.o \
|
||||
ice_adapter.o
|
||||
ice-$(CONFIG_PCI_IOV) += \
|
||||
|
|
|
|||
|
|
@ -568,9 +568,6 @@ struct ice_pf {
|
|||
struct ice_sw *first_sw; /* first switch created by firmware */
|
||||
u16 eswitch_mode; /* current mode of eswitch */
|
||||
struct dentry *ice_debugfs_pf;
|
||||
struct dentry *ice_debugfs_pf_fwlog;
|
||||
/* keep track of all the dentrys for FW log modules */
|
||||
struct dentry **ice_debugfs_pf_fwlog_modules;
|
||||
struct ice_vfs vfs;
|
||||
DECLARE_BITMAP(features, ICE_F_MAX);
|
||||
DECLARE_BITMAP(state, ICE_STATE_NBITS);
|
||||
|
|
@ -908,11 +905,10 @@ static inline bool ice_is_adq_active(struct ice_pf *pf)
|
|||
return false;
|
||||
}
|
||||
|
||||
void ice_debugfs_fwlog_init(struct ice_pf *pf);
|
||||
int ice_debugfs_pf_init(struct ice_pf *pf);
|
||||
void ice_debugfs_pf_deinit(struct ice_pf *pf);
|
||||
void ice_debugfs_init(void);
|
||||
void ice_debugfs_exit(void);
|
||||
void ice_pf_fwlog_update_module(struct ice_pf *pf, int log_level, int module);
|
||||
|
||||
bool netif_is_ice(const struct net_device *dev);
|
||||
int ice_vsi_setup_tx_rings(struct ice_vsi *vsi);
|
||||
|
|
|
|||
|
|
@ -2399,42 +2399,6 @@ struct ice_aqc_event_lan_overflow {
|
|||
u8 reserved[8];
|
||||
};
|
||||
|
||||
enum ice_aqc_fw_logging_mod {
|
||||
ICE_AQC_FW_LOG_ID_GENERAL = 0,
|
||||
ICE_AQC_FW_LOG_ID_CTRL,
|
||||
ICE_AQC_FW_LOG_ID_LINK,
|
||||
ICE_AQC_FW_LOG_ID_LINK_TOPO,
|
||||
ICE_AQC_FW_LOG_ID_DNL,
|
||||
ICE_AQC_FW_LOG_ID_I2C,
|
||||
ICE_AQC_FW_LOG_ID_SDP,
|
||||
ICE_AQC_FW_LOG_ID_MDIO,
|
||||
ICE_AQC_FW_LOG_ID_ADMINQ,
|
||||
ICE_AQC_FW_LOG_ID_HDMA,
|
||||
ICE_AQC_FW_LOG_ID_LLDP,
|
||||
ICE_AQC_FW_LOG_ID_DCBX,
|
||||
ICE_AQC_FW_LOG_ID_DCB,
|
||||
ICE_AQC_FW_LOG_ID_XLR,
|
||||
ICE_AQC_FW_LOG_ID_NVM,
|
||||
ICE_AQC_FW_LOG_ID_AUTH,
|
||||
ICE_AQC_FW_LOG_ID_VPD,
|
||||
ICE_AQC_FW_LOG_ID_IOSF,
|
||||
ICE_AQC_FW_LOG_ID_PARSER,
|
||||
ICE_AQC_FW_LOG_ID_SW,
|
||||
ICE_AQC_FW_LOG_ID_SCHEDULER,
|
||||
ICE_AQC_FW_LOG_ID_TXQ,
|
||||
ICE_AQC_FW_LOG_ID_RSVD,
|
||||
ICE_AQC_FW_LOG_ID_POST,
|
||||
ICE_AQC_FW_LOG_ID_WATCHDOG,
|
||||
ICE_AQC_FW_LOG_ID_TASK_DISPATCH,
|
||||
ICE_AQC_FW_LOG_ID_MNG,
|
||||
ICE_AQC_FW_LOG_ID_SYNCE,
|
||||
ICE_AQC_FW_LOG_ID_HEALTH,
|
||||
ICE_AQC_FW_LOG_ID_TSDRV,
|
||||
ICE_AQC_FW_LOG_ID_PFREG,
|
||||
ICE_AQC_FW_LOG_ID_MDLVER,
|
||||
ICE_AQC_FW_LOG_ID_MAX,
|
||||
};
|
||||
|
||||
enum ice_aqc_health_status_mask {
|
||||
ICE_AQC_HEALTH_STATUS_SET_PF_SPECIFIC_MASK = BIT(0),
|
||||
ICE_AQC_HEALTH_STATUS_SET_ALL_PF_MASK = BIT(1),
|
||||
|
|
@ -2516,48 +2480,6 @@ struct ice_aqc_health_status_elem {
|
|||
__le32 internal_data2;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
#define ICE_AQC_FW_LOG_CONF_UART_EN BIT(0)
|
||||
#define ICE_AQC_FW_LOG_CONF_AQ_EN BIT(1)
|
||||
#define ICE_AQC_FW_LOG_QUERY_REGISTERED BIT(2)
|
||||
#define ICE_AQC_FW_LOG_CONF_SET_VALID BIT(3)
|
||||
#define ICE_AQC_FW_LOG_AQ_REGISTER BIT(0)
|
||||
#define ICE_AQC_FW_LOG_AQ_QUERY BIT(2)
|
||||
|
||||
u8 rsp_flag;
|
||||
__le16 fw_rt_msb;
|
||||
union {
|
||||
struct {
|
||||
__le32 fw_rt_lsb;
|
||||
} sync;
|
||||
struct {
|
||||
__le16 log_resolution;
|
||||
#define ICE_AQC_FW_LOG_MIN_RESOLUTION (1)
|
||||
#define ICE_AQC_FW_LOG_MAX_RESOLUTION (128)
|
||||
|
||||
__le16 mdl_cnt;
|
||||
} cfg;
|
||||
} ops;
|
||||
__le32 addr_high;
|
||||
__le32 addr_low;
|
||||
};
|
||||
|
||||
/* Response Buffer for:
|
||||
* Set Firmware Logging Configuration (0xFF30)
|
||||
* Query FW Logging (0xFF32)
|
||||
*/
|
||||
struct ice_aqc_fw_log_cfg_resp {
|
||||
__le16 module_identifier;
|
||||
u8 log_level;
|
||||
u8 rsvd0;
|
||||
};
|
||||
|
||||
/* Admin Queue command opcodes */
|
||||
enum ice_adminq_opc {
|
||||
/* AQ commands */
|
||||
|
|
|
|||
|
|
@ -984,6 +984,37 @@ static int ice_wait_for_fw(struct ice_hw *hw, u32 timeout)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int __fwlog_send_cmd(void *priv, struct libie_aq_desc *desc, void *buf,
|
||||
u16 size)
|
||||
{
|
||||
struct ice_hw *hw = priv;
|
||||
|
||||
return ice_aq_send_cmd(hw, desc, buf, size, NULL);
|
||||
}
|
||||
|
||||
static int __fwlog_init(struct ice_hw *hw)
|
||||
{
|
||||
struct ice_pf *pf = hw->back;
|
||||
struct libie_fwlog_api api = {
|
||||
.pdev = pf->pdev,
|
||||
.send_cmd = __fwlog_send_cmd,
|
||||
.priv = hw,
|
||||
};
|
||||
int err;
|
||||
|
||||
/* only support fw log commands on PF 0 */
|
||||
if (hw->bus.func)
|
||||
return -EINVAL;
|
||||
|
||||
err = ice_debugfs_pf_init(pf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
api.debugfs_root = pf->ice_debugfs_pf;
|
||||
|
||||
return libie_fwlog_init(&hw->fwlog, &api);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_init_hw - main hardware initialization routine
|
||||
* @hw: pointer to the hardware structure
|
||||
|
|
@ -1012,7 +1043,7 @@ int ice_init_hw(struct ice_hw *hw)
|
|||
if (status)
|
||||
goto err_unroll_cqinit;
|
||||
|
||||
status = ice_fwlog_init(hw);
|
||||
status = __fwlog_init(hw);
|
||||
if (status)
|
||||
ice_debug(hw, ICE_DBG_FW_LOG, "Error initializing FW logging: %d\n",
|
||||
status);
|
||||
|
|
@ -1159,6 +1190,16 @@ int ice_init_hw(struct ice_hw *hw)
|
|||
return status;
|
||||
}
|
||||
|
||||
static void __fwlog_deinit(struct ice_hw *hw)
|
||||
{
|
||||
/* only support fw log commands on PF 0 */
|
||||
if (hw->bus.func)
|
||||
return;
|
||||
|
||||
ice_debugfs_pf_deinit(hw->back);
|
||||
libie_fwlog_deinit(&hw->fwlog);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_deinit_hw - unroll initialization operations done by ice_init_hw
|
||||
* @hw: pointer to the hardware structure
|
||||
|
|
@ -1177,8 +1218,7 @@ void ice_deinit_hw(struct ice_hw *hw)
|
|||
ice_free_seg(hw);
|
||||
ice_free_hw_tbls(hw);
|
||||
mutex_destroy(&hw->tnl_lock);
|
||||
|
||||
ice_fwlog_deinit(hw);
|
||||
__fwlog_deinit(hw);
|
||||
ice_destroy_all_ctrlq(hw);
|
||||
|
||||
/* Clear VSI contexts if not already cleared */
|
||||
|
|
|
|||
|
|
@ -1,647 +1,20 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022, Intel Corporation. */
|
||||
|
||||
#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;
|
||||
|
||||
/* create a define that has an extra module that doesn't really exist. this
|
||||
* is so we can add a module 'all' to easily enable/disable all the modules
|
||||
*/
|
||||
#define ICE_NR_FW_LOG_MODULES (ICE_AQC_FW_LOG_ID_MAX + 1)
|
||||
|
||||
/* the ordering in this array is important. it matches the ordering of the
|
||||
* values in the FW so the index is the same value as in ice_aqc_fw_logging_mod
|
||||
*/
|
||||
static const char * const ice_fwlog_module_string[] = {
|
||||
"general",
|
||||
"ctrl",
|
||||
"link",
|
||||
"link_topo",
|
||||
"dnl",
|
||||
"i2c",
|
||||
"sdp",
|
||||
"mdio",
|
||||
"adminq",
|
||||
"hdma",
|
||||
"lldp",
|
||||
"dcbx",
|
||||
"dcb",
|
||||
"xlr",
|
||||
"nvm",
|
||||
"auth",
|
||||
"vpd",
|
||||
"iosf",
|
||||
"parser",
|
||||
"sw",
|
||||
"scheduler",
|
||||
"txq",
|
||||
"rsvd",
|
||||
"post",
|
||||
"watchdog",
|
||||
"task_dispatch",
|
||||
"mng",
|
||||
"synce",
|
||||
"health",
|
||||
"tsdrv",
|
||||
"pfreg",
|
||||
"mdlver",
|
||||
"all",
|
||||
};
|
||||
|
||||
/* the ordering in this array is important. it matches the ordering of the
|
||||
* values in the FW so the index is the same value as in ice_fwlog_level
|
||||
*/
|
||||
static const char * const ice_fwlog_level_string[] = {
|
||||
"none",
|
||||
"error",
|
||||
"warning",
|
||||
"normal",
|
||||
"verbose",
|
||||
};
|
||||
|
||||
static const char * const ice_fwlog_log_size[] = {
|
||||
"128K",
|
||||
"256K",
|
||||
"512K",
|
||||
"1M",
|
||||
"2M",
|
||||
};
|
||||
|
||||
/**
|
||||
* ice_fwlog_print_module_cfg - print current FW logging module configuration
|
||||
* @hw: pointer to the HW structure
|
||||
* @module: module to print
|
||||
* @s: the seq file to put data into
|
||||
*/
|
||||
static void
|
||||
ice_fwlog_print_module_cfg(struct ice_hw *hw, int module, struct seq_file *s)
|
||||
{
|
||||
struct ice_fwlog_cfg *cfg = &hw->fwlog_cfg;
|
||||
struct ice_fwlog_module_entry *entry;
|
||||
|
||||
if (module != ICE_AQC_FW_LOG_ID_MAX) {
|
||||
entry = &cfg->module_entries[module];
|
||||
|
||||
seq_printf(s, "\tModule: %s, Log Level: %s\n",
|
||||
ice_fwlog_module_string[entry->module_id],
|
||||
ice_fwlog_level_string[entry->log_level]);
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ICE_AQC_FW_LOG_ID_MAX; i++) {
|
||||
entry = &cfg->module_entries[i];
|
||||
|
||||
seq_printf(s, "\tModule: %s, Log Level: %s\n",
|
||||
ice_fwlog_module_string[entry->module_id],
|
||||
ice_fwlog_level_string[entry->log_level]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ice_find_module_by_dentry(struct ice_pf *pf, struct dentry *d)
|
||||
{
|
||||
int i, module;
|
||||
|
||||
module = -1;
|
||||
/* find the module based on the dentry */
|
||||
for (i = 0; i < ICE_NR_FW_LOG_MODULES; i++) {
|
||||
if (d == pf->ice_debugfs_pf_fwlog_modules[i]) {
|
||||
module = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_debugfs_module_show - read from 'module' file
|
||||
* @s: the opened file
|
||||
* @v: pointer to the offset
|
||||
*/
|
||||
static int ice_debugfs_module_show(struct seq_file *s, void *v)
|
||||
{
|
||||
const struct file *filp = s->file;
|
||||
struct dentry *dentry;
|
||||
struct ice_pf *pf;
|
||||
int module;
|
||||
|
||||
dentry = file_dentry(filp);
|
||||
pf = s->private;
|
||||
|
||||
module = ice_find_module_by_dentry(pf, dentry);
|
||||
if (module < 0) {
|
||||
dev_info(ice_pf_to_dev(pf), "unknown module\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ice_fwlog_print_module_cfg(&pf->hw, module, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ice_debugfs_module_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return single_open(filp, ice_debugfs_module_show, inode->i_private);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_debugfs_module_write - write into 'module' file
|
||||
* @filp: the opened file
|
||||
* @buf: where to find the user's data
|
||||
* @count: the length of the user's data
|
||||
* @ppos: file position offset
|
||||
*/
|
||||
static ssize_t
|
||||
ice_debugfs_module_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = file_inode(filp)->i_private;
|
||||
struct dentry *dentry = file_dentry(filp);
|
||||
struct device *dev = ice_pf_to_dev(pf);
|
||||
char user_val[16], *cmd_buf;
|
||||
int module, log_level, cnt;
|
||||
|
||||
/* don't allow partial writes or invalid input */
|
||||
if (*ppos != 0 || count > 8)
|
||||
return -EINVAL;
|
||||
|
||||
cmd_buf = memdup_user_nul(buf, count);
|
||||
if (IS_ERR(cmd_buf))
|
||||
return PTR_ERR(cmd_buf);
|
||||
|
||||
module = ice_find_module_by_dentry(pf, dentry);
|
||||
if (module < 0) {
|
||||
dev_info(dev, "unknown module\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cnt = sscanf(cmd_buf, "%s", user_val);
|
||||
if (cnt != 1)
|
||||
return -EINVAL;
|
||||
|
||||
log_level = sysfs_match_string(ice_fwlog_level_string, user_val);
|
||||
if (log_level < 0) {
|
||||
dev_info(dev, "unknown log level '%s'\n", user_val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (module != ICE_AQC_FW_LOG_ID_MAX) {
|
||||
ice_pf_fwlog_update_module(pf, log_level, module);
|
||||
} else {
|
||||
/* the module 'all' is a shortcut so that we can set
|
||||
* all of the modules to the same level quickly
|
||||
*/
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ICE_AQC_FW_LOG_ID_MAX; i++)
|
||||
ice_pf_fwlog_update_module(pf, log_level, i);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations ice_debugfs_module_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ice_debugfs_module_open,
|
||||
.read = seq_read,
|
||||
.release = single_release,
|
||||
.write = ice_debugfs_module_write,
|
||||
};
|
||||
|
||||
/**
|
||||
* ice_debugfs_nr_messages_read - read from 'nr_messages' file
|
||||
* @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_nr_messages_read(struct file *filp,
|
||||
char __user *buffer, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = filp->private_data;
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
char buff[32] = {};
|
||||
|
||||
snprintf(buff, sizeof(buff), "%d\n",
|
||||
hw->fwlog_cfg.log_resolution);
|
||||
|
||||
return simple_read_from_buffer(buffer, count, ppos, buff, strlen(buff));
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_debugfs_nr_messages_write - write into 'nr_messages' file
|
||||
* @filp: the opened file
|
||||
* @buf: where to find the user's data
|
||||
* @count: the length of the user's data
|
||||
* @ppos: file position offset
|
||||
*/
|
||||
static ssize_t
|
||||
ice_debugfs_nr_messages_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = filp->private_data;
|
||||
struct device *dev = ice_pf_to_dev(pf);
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
char user_val[8], *cmd_buf;
|
||||
s16 nr_messages;
|
||||
ssize_t ret;
|
||||
|
||||
/* don't allow partial writes or invalid input */
|
||||
if (*ppos != 0 || count > 4)
|
||||
return -EINVAL;
|
||||
|
||||
cmd_buf = memdup_user_nul(buf, count);
|
||||
if (IS_ERR(cmd_buf))
|
||||
return PTR_ERR(cmd_buf);
|
||||
|
||||
ret = sscanf(cmd_buf, "%s", user_val);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kstrtos16(user_val, 0, &nr_messages);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nr_messages < ICE_AQC_FW_LOG_MIN_RESOLUTION ||
|
||||
nr_messages > ICE_AQC_FW_LOG_MAX_RESOLUTION) {
|
||||
dev_err(dev, "Invalid FW log number of messages %d, value must be between %d - %d\n",
|
||||
nr_messages, ICE_AQC_FW_LOG_MIN_RESOLUTION,
|
||||
ICE_AQC_FW_LOG_MAX_RESOLUTION);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hw->fwlog_cfg.log_resolution = nr_messages;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations ice_debugfs_nr_messages_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = ice_debugfs_nr_messages_read,
|
||||
.write = ice_debugfs_nr_messages_write,
|
||||
};
|
||||
|
||||
/**
|
||||
* ice_debugfs_enable_read - read from 'enable' file
|
||||
* @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_enable_read(struct file *filp,
|
||||
char __user *buffer, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = filp->private_data;
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
char buff[32] = {};
|
||||
|
||||
snprintf(buff, sizeof(buff), "%u\n",
|
||||
(u16)(hw->fwlog_cfg.options &
|
||||
ICE_FWLOG_OPTION_IS_REGISTERED) >> 3);
|
||||
|
||||
return simple_read_from_buffer(buffer, count, ppos, buff, strlen(buff));
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_debugfs_enable_write - write into 'enable' file
|
||||
* @filp: the opened file
|
||||
* @buf: where to find the user's data
|
||||
* @count: the length of the user's data
|
||||
* @ppos: file position offset
|
||||
*/
|
||||
static ssize_t
|
||||
ice_debugfs_enable_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = filp->private_data;
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
char user_val[8], *cmd_buf;
|
||||
bool enable;
|
||||
ssize_t ret;
|
||||
|
||||
/* don't allow partial writes or invalid input */
|
||||
if (*ppos != 0 || count > 2)
|
||||
return -EINVAL;
|
||||
|
||||
cmd_buf = memdup_user_nul(buf, count);
|
||||
if (IS_ERR(cmd_buf))
|
||||
return PTR_ERR(cmd_buf);
|
||||
|
||||
ret = sscanf(cmd_buf, "%s", user_val);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kstrtobool(user_val, &enable);
|
||||
if (ret)
|
||||
goto enable_write_error;
|
||||
|
||||
if (enable)
|
||||
hw->fwlog_cfg.options |= ICE_FWLOG_OPTION_ARQ_ENA;
|
||||
else
|
||||
hw->fwlog_cfg.options &= ~ICE_FWLOG_OPTION_ARQ_ENA;
|
||||
|
||||
ret = ice_fwlog_set(hw, &hw->fwlog_cfg);
|
||||
if (ret)
|
||||
goto enable_write_error;
|
||||
|
||||
if (enable)
|
||||
ret = ice_fwlog_register(hw);
|
||||
else
|
||||
ret = ice_fwlog_unregister(hw);
|
||||
|
||||
if (ret)
|
||||
goto enable_write_error;
|
||||
|
||||
/* if we get here, nothing went wrong; return count since we didn't
|
||||
* really write anything
|
||||
*/
|
||||
ret = (ssize_t)count;
|
||||
|
||||
enable_write_error:
|
||||
/* This function always consumes all of the written input, or produces
|
||||
* an error. Check and enforce this. Otherwise, the write operation
|
||||
* won't complete properly.
|
||||
*/
|
||||
if (WARN_ON(ret != (ssize_t)count && ret >= 0))
|
||||
ret = -EIO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations ice_debugfs_enable_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = ice_debugfs_enable_read,
|
||||
.write = ice_debugfs_enable_write,
|
||||
};
|
||||
|
||||
/**
|
||||
* ice_debugfs_log_size_read - read from 'log_size' file
|
||||
* @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_log_size_read(struct file *filp,
|
||||
char __user *buffer, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = filp->private_data;
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
char buff[32] = {};
|
||||
int index;
|
||||
|
||||
index = hw->fwlog_ring.index;
|
||||
snprintf(buff, sizeof(buff), "%s\n", ice_fwlog_log_size[index]);
|
||||
|
||||
return simple_read_from_buffer(buffer, count, ppos, buff, strlen(buff));
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_debugfs_log_size_write - write into 'log_size' file
|
||||
* @filp: the opened file
|
||||
* @buf: where to find the user's data
|
||||
* @count: the length of the user's data
|
||||
* @ppos: file position offset
|
||||
*/
|
||||
static ssize_t
|
||||
ice_debugfs_log_size_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = filp->private_data;
|
||||
struct device *dev = ice_pf_to_dev(pf);
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
char user_val[8], *cmd_buf;
|
||||
ssize_t ret;
|
||||
int index;
|
||||
|
||||
/* don't allow partial writes or invalid input */
|
||||
if (*ppos != 0 || count > 5)
|
||||
return -EINVAL;
|
||||
|
||||
cmd_buf = memdup_user_nul(buf, count);
|
||||
if (IS_ERR(cmd_buf))
|
||||
return PTR_ERR(cmd_buf);
|
||||
|
||||
ret = sscanf(cmd_buf, "%s", user_val);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
index = sysfs_match_string(ice_fwlog_log_size, user_val);
|
||||
if (index < 0) {
|
||||
dev_info(dev, "Invalid log size '%s'. The value must be one of 128K, 256K, 512K, 1M, 2M\n",
|
||||
user_val);
|
||||
ret = -EINVAL;
|
||||
goto log_size_write_error;
|
||||
} else if (hw->fwlog_cfg.options & ICE_FWLOG_OPTION_IS_REGISTERED) {
|
||||
dev_info(dev, "FW logging is currently running. Please disable FW logging to change log_size\n");
|
||||
ret = -EINVAL;
|
||||
goto log_size_write_error;
|
||||
}
|
||||
|
||||
/* free all the buffers and the tracking info and resize */
|
||||
ice_fwlog_realloc_rings(hw, index);
|
||||
|
||||
/* if we get here, nothing went wrong; return count since we didn't
|
||||
* really write anything
|
||||
*/
|
||||
ret = (ssize_t)count;
|
||||
|
||||
log_size_write_error:
|
||||
/* This function always consumes all of the written input, or produces
|
||||
* an error. Check and enforce this. Otherwise, the write operation
|
||||
* won't complete properly.
|
||||
*/
|
||||
if (WARN_ON(ret != (ssize_t)count && ret >= 0))
|
||||
ret = -EIO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations ice_debugfs_log_size_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = ice_debugfs_log_size_read,
|
||||
.write = ice_debugfs_log_size_write,
|
||||
};
|
||||
|
||||
/**
|
||||
* ice_debugfs_data_read - read from 'data' file
|
||||
* @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_data_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = filp->private_data;
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
int data_copied = 0;
|
||||
bool done = false;
|
||||
|
||||
if (ice_fwlog_ring_empty(&hw->fwlog_ring))
|
||||
return 0;
|
||||
|
||||
while (!ice_fwlog_ring_empty(&hw->fwlog_ring) && !done) {
|
||||
struct ice_fwlog_data *log;
|
||||
u16 cur_buf_len;
|
||||
|
||||
log = &hw->fwlog_ring.rings[hw->fwlog_ring.head];
|
||||
cur_buf_len = log->data_size;
|
||||
if (cur_buf_len >= count) {
|
||||
done = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (copy_to_user(buffer, log->data, cur_buf_len)) {
|
||||
/* if there is an error then bail and return whatever
|
||||
* the driver has copied so far
|
||||
*/
|
||||
done = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
data_copied += cur_buf_len;
|
||||
buffer += cur_buf_len;
|
||||
count -= cur_buf_len;
|
||||
*ppos += cur_buf_len;
|
||||
ice_fwlog_ring_increment(&hw->fwlog_ring.head,
|
||||
hw->fwlog_ring.size);
|
||||
}
|
||||
|
||||
return data_copied;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_debugfs_data_write - write into 'data' file
|
||||
* @filp: the opened file
|
||||
* @buf: where to find the user's data
|
||||
* @count: the length of the user's data
|
||||
* @ppos: file position offset
|
||||
*/
|
||||
static ssize_t
|
||||
ice_debugfs_data_write(struct file *filp, const char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct ice_pf *pf = filp->private_data;
|
||||
struct device *dev = ice_pf_to_dev(pf);
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
ssize_t ret;
|
||||
|
||||
/* don't allow partial writes */
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
/* any value is allowed to clear the buffer so no need to even look at
|
||||
* what the value is
|
||||
*/
|
||||
if (!(hw->fwlog_cfg.options & ICE_FWLOG_OPTION_IS_REGISTERED)) {
|
||||
hw->fwlog_ring.head = 0;
|
||||
hw->fwlog_ring.tail = 0;
|
||||
} else {
|
||||
dev_info(dev, "Can't clear FW log data while FW log running\n");
|
||||
ret = -EINVAL;
|
||||
goto nr_buffs_write_error;
|
||||
}
|
||||
|
||||
/* if we get here, nothing went wrong; return count since we didn't
|
||||
* really write anything
|
||||
*/
|
||||
ret = (ssize_t)count;
|
||||
|
||||
nr_buffs_write_error:
|
||||
/* This function always consumes all of the written input, or produces
|
||||
* an error. Check and enforce this. Otherwise, the write operation
|
||||
* won't complete properly.
|
||||
*/
|
||||
if (WARN_ON(ret != (ssize_t)count && ret >= 0))
|
||||
ret = -EIO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations ice_debugfs_data_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = ice_debugfs_data_read,
|
||||
.write = ice_debugfs_data_write,
|
||||
};
|
||||
|
||||
/**
|
||||
* ice_debugfs_fwlog_init - setup the debugfs directory
|
||||
* @pf: the ice that is starting up
|
||||
*/
|
||||
void ice_debugfs_fwlog_init(struct ice_pf *pf)
|
||||
int ice_debugfs_pf_init(struct ice_pf *pf)
|
||||
{
|
||||
const char *name = pci_name(pf->pdev);
|
||||
struct dentry *fw_modules_dir;
|
||||
struct dentry **fw_modules;
|
||||
int i;
|
||||
|
||||
/* only support fw log commands on PF 0 */
|
||||
if (pf->hw.bus.func)
|
||||
return;
|
||||
|
||||
/* allocate space for this first because if it fails then we don't
|
||||
* need to unwind
|
||||
*/
|
||||
fw_modules = kcalloc(ICE_NR_FW_LOG_MODULES, sizeof(*fw_modules),
|
||||
GFP_KERNEL);
|
||||
if (!fw_modules)
|
||||
return;
|
||||
|
||||
pf->ice_debugfs_pf = debugfs_create_dir(name, ice_debugfs_root);
|
||||
if (IS_ERR(pf->ice_debugfs_pf))
|
||||
goto err_create_module_files;
|
||||
return PTR_ERR(pf->ice_debugfs_pf);
|
||||
|
||||
pf->ice_debugfs_pf_fwlog = debugfs_create_dir("fwlog",
|
||||
pf->ice_debugfs_pf);
|
||||
if (IS_ERR(pf->ice_debugfs_pf_fwlog))
|
||||
goto err_create_module_files;
|
||||
|
||||
fw_modules_dir = debugfs_create_dir("modules",
|
||||
pf->ice_debugfs_pf_fwlog);
|
||||
if (IS_ERR(fw_modules_dir))
|
||||
goto err_create_module_files;
|
||||
|
||||
for (i = 0; i < ICE_NR_FW_LOG_MODULES; i++) {
|
||||
fw_modules[i] = debugfs_create_file(ice_fwlog_module_string[i],
|
||||
0600, fw_modules_dir, pf,
|
||||
&ice_debugfs_module_fops);
|
||||
if (IS_ERR(fw_modules[i]))
|
||||
goto err_create_module_files;
|
||||
}
|
||||
|
||||
debugfs_create_file("nr_messages", 0600,
|
||||
pf->ice_debugfs_pf_fwlog, pf,
|
||||
&ice_debugfs_nr_messages_fops);
|
||||
|
||||
pf->ice_debugfs_pf_fwlog_modules = fw_modules;
|
||||
|
||||
debugfs_create_file("enable", 0600, pf->ice_debugfs_pf_fwlog,
|
||||
pf, &ice_debugfs_enable_fops);
|
||||
|
||||
debugfs_create_file("log_size", 0600, pf->ice_debugfs_pf_fwlog,
|
||||
pf, &ice_debugfs_log_size_fops);
|
||||
|
||||
debugfs_create_file("data", 0600, pf->ice_debugfs_pf_fwlog,
|
||||
pf, &ice_debugfs_data_fops);
|
||||
|
||||
return;
|
||||
|
||||
err_create_module_files:
|
||||
debugfs_remove_recursive(pf->ice_debugfs_pf_fwlog);
|
||||
kfree(fw_modules);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,474 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022, Intel Corporation. */
|
||||
|
||||
#include <linux/vmalloc.h>
|
||||
#include "ice.h"
|
||||
#include "ice_common.h"
|
||||
#include "ice_fwlog.h"
|
||||
|
||||
bool ice_fwlog_ring_full(struct ice_fwlog_ring *rings)
|
||||
{
|
||||
u16 head, tail;
|
||||
|
||||
head = rings->head;
|
||||
tail = rings->tail;
|
||||
|
||||
if (head < tail && (tail - head == (rings->size - 1)))
|
||||
return true;
|
||||
else if (head > tail && (tail == (head - 1)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ice_fwlog_ring_empty(struct ice_fwlog_ring *rings)
|
||||
{
|
||||
return rings->head == rings->tail;
|
||||
}
|
||||
|
||||
void ice_fwlog_ring_increment(u16 *item, u16 size)
|
||||
{
|
||||
*item = (*item + 1) & (size - 1);
|
||||
}
|
||||
|
||||
static int ice_fwlog_alloc_ring_buffs(struct ice_fwlog_ring *rings)
|
||||
{
|
||||
int i, nr_bytes;
|
||||
u8 *mem;
|
||||
|
||||
nr_bytes = rings->size * ICE_AQ_MAX_BUF_LEN;
|
||||
mem = vzalloc(nr_bytes);
|
||||
if (!mem)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < rings->size; i++) {
|
||||
struct ice_fwlog_data *ring = &rings->rings[i];
|
||||
|
||||
ring->data_size = ICE_AQ_MAX_BUF_LEN;
|
||||
ring->data = mem;
|
||||
mem += ICE_AQ_MAX_BUF_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ice_fwlog_free_ring_buffs(struct ice_fwlog_ring *rings)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < rings->size; i++) {
|
||||
struct ice_fwlog_data *ring = &rings->rings[i];
|
||||
|
||||
/* the first ring is the base memory for the whole range so
|
||||
* free it
|
||||
*/
|
||||
if (!i)
|
||||
vfree(ring->data);
|
||||
|
||||
ring->data = NULL;
|
||||
ring->data_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define ICE_FWLOG_INDEX_TO_BYTES(n) ((128 * 1024) << (n))
|
||||
/**
|
||||
* ice_fwlog_realloc_rings - reallocate the FW log rings
|
||||
* @hw: pointer to the HW structure
|
||||
* @index: the new index to use to allocate memory for the log data
|
||||
*
|
||||
*/
|
||||
void ice_fwlog_realloc_rings(struct ice_hw *hw, int index)
|
||||
{
|
||||
struct ice_fwlog_ring ring;
|
||||
int status, ring_size;
|
||||
|
||||
/* convert the number of bytes into a number of 4K buffers. externally
|
||||
* the driver presents the interface to the FW log data as a number of
|
||||
* bytes because that's easy for users to understand. internally the
|
||||
* driver uses a ring of buffers because the driver doesn't know where
|
||||
* the beginning and end of any line of log data is so the driver has
|
||||
* to overwrite data as complete blocks. when the data is returned to
|
||||
* the user the driver knows that the data is correct and the FW log
|
||||
* can be correctly parsed by the tools
|
||||
*/
|
||||
ring_size = ICE_FWLOG_INDEX_TO_BYTES(index) / ICE_AQ_MAX_BUF_LEN;
|
||||
if (ring_size == hw->fwlog_ring.size)
|
||||
return;
|
||||
|
||||
/* allocate space for the new rings and buffers then release the
|
||||
* old rings and buffers. that way if we don't have enough
|
||||
* memory then we at least have what we had before
|
||||
*/
|
||||
ring.rings = kcalloc(ring_size, sizeof(*ring.rings), GFP_KERNEL);
|
||||
if (!ring.rings)
|
||||
return;
|
||||
|
||||
ring.size = ring_size;
|
||||
|
||||
status = ice_fwlog_alloc_ring_buffs(&ring);
|
||||
if (status) {
|
||||
dev_warn(ice_hw_to_dev(hw), "Unable to allocate memory for FW log ring data buffers\n");
|
||||
ice_fwlog_free_ring_buffs(&ring);
|
||||
kfree(ring.rings);
|
||||
return;
|
||||
}
|
||||
|
||||
ice_fwlog_free_ring_buffs(&hw->fwlog_ring);
|
||||
kfree(hw->fwlog_ring.rings);
|
||||
|
||||
hw->fwlog_ring.rings = ring.rings;
|
||||
hw->fwlog_ring.size = ring.size;
|
||||
hw->fwlog_ring.index = index;
|
||||
hw->fwlog_ring.head = 0;
|
||||
hw->fwlog_ring.tail = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fwlog_init - Initialize FW logging configuration
|
||||
* @hw: pointer to the HW structure
|
||||
*
|
||||
* This function should be called on driver initialization during
|
||||
* ice_init_hw().
|
||||
*/
|
||||
int ice_fwlog_init(struct ice_hw *hw)
|
||||
{
|
||||
/* only support fw log commands on PF 0 */
|
||||
if (hw->bus.func)
|
||||
return -EINVAL;
|
||||
|
||||
ice_fwlog_set_supported(hw);
|
||||
|
||||
if (ice_fwlog_supported(hw)) {
|
||||
int status;
|
||||
|
||||
/* read the current config from the FW and store it */
|
||||
status = ice_fwlog_get(hw, &hw->fwlog_cfg);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
hw->fwlog_ring.rings = kcalloc(ICE_FWLOG_RING_SIZE_DFLT,
|
||||
sizeof(*hw->fwlog_ring.rings),
|
||||
GFP_KERNEL);
|
||||
if (!hw->fwlog_ring.rings) {
|
||||
dev_warn(ice_hw_to_dev(hw), "Unable to allocate memory for FW log rings\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hw->fwlog_ring.size = ICE_FWLOG_RING_SIZE_DFLT;
|
||||
hw->fwlog_ring.index = ICE_FWLOG_RING_SIZE_INDEX_DFLT;
|
||||
|
||||
status = ice_fwlog_alloc_ring_buffs(&hw->fwlog_ring);
|
||||
if (status) {
|
||||
dev_warn(ice_hw_to_dev(hw), "Unable to allocate memory for FW log ring data buffers\n");
|
||||
ice_fwlog_free_ring_buffs(&hw->fwlog_ring);
|
||||
kfree(hw->fwlog_ring.rings);
|
||||
return status;
|
||||
}
|
||||
|
||||
ice_debugfs_fwlog_init(hw->back);
|
||||
} else {
|
||||
dev_warn(ice_hw_to_dev(hw), "FW logging is not supported in this NVM image. Please update the NVM to get FW log support\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fwlog_deinit - unroll FW logging configuration
|
||||
* @hw: pointer to the HW structure
|
||||
*
|
||||
* This function should be called in ice_deinit_hw().
|
||||
*/
|
||||
void ice_fwlog_deinit(struct ice_hw *hw)
|
||||
{
|
||||
struct ice_pf *pf = hw->back;
|
||||
int status;
|
||||
|
||||
/* only support fw log commands on PF 0 */
|
||||
if (hw->bus.func)
|
||||
return;
|
||||
|
||||
ice_debugfs_pf_deinit(hw->back);
|
||||
|
||||
/* 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;
|
||||
status = ice_fwlog_set(hw, &hw->fwlog_cfg);
|
||||
if (status)
|
||||
dev_warn(ice_hw_to_dev(hw), "Unable to turn off FW logging, status: %d\n",
|
||||
status);
|
||||
|
||||
kfree(pf->ice_debugfs_pf_fwlog_modules);
|
||||
|
||||
pf->ice_debugfs_pf_fwlog_modules = NULL;
|
||||
|
||||
status = ice_fwlog_unregister(hw);
|
||||
if (status)
|
||||
dev_warn(ice_hw_to_dev(hw), "Unable to unregister FW logging, status: %d\n",
|
||||
status);
|
||||
|
||||
if (hw->fwlog_ring.rings) {
|
||||
ice_fwlog_free_ring_buffs(&hw->fwlog_ring);
|
||||
kfree(hw->fwlog_ring.rings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fwlog_supported - Cached for whether FW supports FW logging or not
|
||||
* @hw: pointer to the HW structure
|
||||
*
|
||||
* This will always return false if called before ice_init_hw(), so it must be
|
||||
* called after ice_init_hw().
|
||||
*/
|
||||
bool ice_fwlog_supported(struct ice_hw *hw)
|
||||
{
|
||||
return hw->fwlog_supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_aq_fwlog_set - Set FW logging configuration AQ command (0xFF30)
|
||||
* @hw: pointer to the HW structure
|
||||
* @entries: entries to configure
|
||||
* @num_entries: number of @entries
|
||||
* @options: options from ice_fwlog_cfg->options structure
|
||||
* @log_resolution: logging resolution
|
||||
*/
|
||||
static int
|
||||
ice_aq_fwlog_set(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
|
||||
u16 num_entries, u16 options, u16 log_resolution)
|
||||
{
|
||||
struct ice_aqc_fw_log_cfg_resp *fw_modules;
|
||||
struct ice_aqc_fw_log *cmd;
|
||||
struct libie_aq_desc desc;
|
||||
int status;
|
||||
int i;
|
||||
|
||||
fw_modules = kcalloc(num_entries, sizeof(*fw_modules), GFP_KERNEL);
|
||||
if (!fw_modules)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
fw_modules[i].module_identifier =
|
||||
cpu_to_le16(entries[i].module_id);
|
||||
fw_modules[i].log_level = entries[i].log_level;
|
||||
}
|
||||
|
||||
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_config);
|
||||
desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_RD);
|
||||
|
||||
cmd = libie_aq_raw(&desc);
|
||||
|
||||
cmd->cmd_flags = ICE_AQC_FW_LOG_CONF_SET_VALID;
|
||||
cmd->ops.cfg.log_resolution = cpu_to_le16(log_resolution);
|
||||
cmd->ops.cfg.mdl_cnt = cpu_to_le16(num_entries);
|
||||
|
||||
if (options & ICE_FWLOG_OPTION_ARQ_ENA)
|
||||
cmd->cmd_flags |= ICE_AQC_FW_LOG_CONF_AQ_EN;
|
||||
if (options & ICE_FWLOG_OPTION_UART_ENA)
|
||||
cmd->cmd_flags |= ICE_AQC_FW_LOG_CONF_UART_EN;
|
||||
|
||||
status = ice_aq_send_cmd(hw, &desc, fw_modules,
|
||||
sizeof(*fw_modules) * num_entries,
|
||||
NULL);
|
||||
|
||||
kfree(fw_modules);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fwlog_set - Set the firmware logging settings
|
||||
* @hw: pointer to the HW structure
|
||||
* @cfg: config used to set firmware logging
|
||||
*
|
||||
* This function should be called whenever the driver needs to set the firmware
|
||||
* logging configuration. It can be called on initialization, reset, or during
|
||||
* runtime.
|
||||
*
|
||||
* If the PF wishes to receive FW logging then it must register via
|
||||
* ice_fwlog_register. Note, that ice_fwlog_register does not need to be called
|
||||
* for init.
|
||||
*/
|
||||
int ice_fwlog_set(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
|
||||
{
|
||||
if (!ice_fwlog_supported(hw))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ice_aq_fwlog_set(hw, cfg->module_entries,
|
||||
ICE_AQC_FW_LOG_ID_MAX, cfg->options,
|
||||
cfg->log_resolution);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_aq_fwlog_get - Get the current firmware logging configuration (0xFF32)
|
||||
* @hw: pointer to the HW structure
|
||||
* @cfg: firmware logging configuration to populate
|
||||
*/
|
||||
static int ice_aq_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
|
||||
{
|
||||
struct ice_aqc_fw_log_cfg_resp *fw_modules;
|
||||
struct ice_aqc_fw_log *cmd;
|
||||
struct libie_aq_desc desc;
|
||||
u16 module_id_cnt;
|
||||
int status;
|
||||
void *buf;
|
||||
int i;
|
||||
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
|
||||
buf = kzalloc(ICE_AQ_MAX_BUF_LEN, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_query);
|
||||
cmd = libie_aq_raw(&desc);
|
||||
|
||||
cmd->cmd_flags = ICE_AQC_FW_LOG_AQ_QUERY;
|
||||
|
||||
status = ice_aq_send_cmd(hw, &desc, buf, ICE_AQ_MAX_BUF_LEN, NULL);
|
||||
if (status) {
|
||||
ice_debug(hw, ICE_DBG_FW_LOG, "Failed to get FW log configuration\n");
|
||||
goto status_out;
|
||||
}
|
||||
|
||||
module_id_cnt = le16_to_cpu(cmd->ops.cfg.mdl_cnt);
|
||||
if (module_id_cnt < ICE_AQC_FW_LOG_ID_MAX) {
|
||||
ice_debug(hw, ICE_DBG_FW_LOG, "FW returned less than the expected number of FW log module IDs\n");
|
||||
} else if (module_id_cnt > ICE_AQC_FW_LOG_ID_MAX) {
|
||||
ice_debug(hw, ICE_DBG_FW_LOG, "FW returned more than expected number of FW log module IDs, setting module_id_cnt to software expected max %u\n",
|
||||
ICE_AQC_FW_LOG_ID_MAX);
|
||||
module_id_cnt = ICE_AQC_FW_LOG_ID_MAX;
|
||||
}
|
||||
|
||||
cfg->log_resolution = le16_to_cpu(cmd->ops.cfg.log_resolution);
|
||||
if (cmd->cmd_flags & ICE_AQC_FW_LOG_CONF_AQ_EN)
|
||||
cfg->options |= ICE_FWLOG_OPTION_ARQ_ENA;
|
||||
if (cmd->cmd_flags & ICE_AQC_FW_LOG_CONF_UART_EN)
|
||||
cfg->options |= ICE_FWLOG_OPTION_UART_ENA;
|
||||
if (cmd->cmd_flags & ICE_AQC_FW_LOG_QUERY_REGISTERED)
|
||||
cfg->options |= ICE_FWLOG_OPTION_IS_REGISTERED;
|
||||
|
||||
fw_modules = (struct ice_aqc_fw_log_cfg_resp *)buf;
|
||||
|
||||
for (i = 0; i < module_id_cnt; i++) {
|
||||
struct ice_aqc_fw_log_cfg_resp *fw_module = &fw_modules[i];
|
||||
|
||||
cfg->module_entries[i].module_id =
|
||||
le16_to_cpu(fw_module->module_identifier);
|
||||
cfg->module_entries[i].log_level = fw_module->log_level;
|
||||
}
|
||||
|
||||
status_out:
|
||||
kfree(buf);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fwlog_get - Get the firmware logging settings
|
||||
* @hw: pointer to the HW structure
|
||||
* @cfg: config to populate based on current firmware logging settings
|
||||
*/
|
||||
int ice_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
|
||||
{
|
||||
if (!ice_fwlog_supported(hw))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ice_aq_fwlog_get(hw, cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_aq_fwlog_register - Register PF for firmware logging events (0xFF31)
|
||||
* @hw: pointer to the HW structure
|
||||
* @reg: true to register and false to unregister
|
||||
*/
|
||||
static int ice_aq_fwlog_register(struct ice_hw *hw, bool reg)
|
||||
{
|
||||
struct ice_aqc_fw_log *cmd;
|
||||
struct libie_aq_desc desc;
|
||||
|
||||
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_register);
|
||||
cmd = libie_aq_raw(&desc);
|
||||
|
||||
if (reg)
|
||||
cmd->cmd_flags = ICE_AQC_FW_LOG_AQ_REGISTER;
|
||||
|
||||
return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fwlog_register - Register the PF for firmware logging
|
||||
* @hw: pointer to the HW structure
|
||||
*
|
||||
* After this call the PF will start to receive firmware logging based on the
|
||||
* configuration set in ice_fwlog_set.
|
||||
*/
|
||||
int ice_fwlog_register(struct ice_hw *hw)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!ice_fwlog_supported(hw))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
status = ice_aq_fwlog_register(hw, true);
|
||||
if (status)
|
||||
ice_debug(hw, ICE_DBG_FW_LOG, "Failed to register for firmware logging events over ARQ\n");
|
||||
else
|
||||
hw->fwlog_cfg.options |= ICE_FWLOG_OPTION_IS_REGISTERED;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fwlog_unregister - Unregister the PF from firmware logging
|
||||
* @hw: pointer to the HW structure
|
||||
*/
|
||||
int ice_fwlog_unregister(struct ice_hw *hw)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!ice_fwlog_supported(hw))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
status = ice_aq_fwlog_register(hw, false);
|
||||
if (status)
|
||||
ice_debug(hw, ICE_DBG_FW_LOG, "Failed to unregister from firmware logging events over ARQ\n");
|
||||
else
|
||||
hw->fwlog_cfg.options &= ~ICE_FWLOG_OPTION_IS_REGISTERED;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fwlog_set_supported - Set if FW logging is supported by FW
|
||||
* @hw: pointer to the HW struct
|
||||
*
|
||||
* If FW returns success to the ice_aq_fwlog_get call then it supports FW
|
||||
* logging, else it doesn't. Set the fwlog_supported flag accordingly.
|
||||
*
|
||||
* This function is only meant to be called during driver init to determine if
|
||||
* the FW support FW logging.
|
||||
*/
|
||||
void ice_fwlog_set_supported(struct ice_hw *hw)
|
||||
{
|
||||
struct ice_fwlog_cfg *cfg;
|
||||
int status;
|
||||
|
||||
hw->fwlog_supported = false;
|
||||
|
||||
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||
if (!cfg)
|
||||
return;
|
||||
|
||||
/* don't call ice_fwlog_get() because that would check to see if FW
|
||||
* logging is supported which is what the driver is determining now
|
||||
*/
|
||||
status = ice_aq_fwlog_get(hw, cfg);
|
||||
if (status)
|
||||
ice_debug(hw, ICE_DBG_FW_LOG, "ice_aq_fwlog_get failed, FW logging is not supported on this version of FW, status %d\n",
|
||||
status);
|
||||
else
|
||||
hw->fwlog_supported = true;
|
||||
|
||||
kfree(cfg);
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2022, Intel Corporation. */
|
||||
|
||||
#ifndef _ICE_FWLOG_H_
|
||||
#define _ICE_FWLOG_H_
|
||||
#include "ice_adminq_cmd.h"
|
||||
|
||||
struct ice_hw;
|
||||
|
||||
/* Only a single log level should be set and all log levels under the set value
|
||||
* are enabled, e.g. if log level is set to ICE_FW_LOG_LEVEL_VERBOSE, then all
|
||||
* other log levels are included (except ICE_FW_LOG_LEVEL_NONE)
|
||||
*/
|
||||
enum ice_fwlog_level {
|
||||
ICE_FWLOG_LEVEL_NONE = 0,
|
||||
ICE_FWLOG_LEVEL_ERROR = 1,
|
||||
ICE_FWLOG_LEVEL_WARNING = 2,
|
||||
ICE_FWLOG_LEVEL_NORMAL = 3,
|
||||
ICE_FWLOG_LEVEL_VERBOSE = 4,
|
||||
ICE_FWLOG_LEVEL_INVALID, /* all values >= this entry are invalid */
|
||||
};
|
||||
|
||||
struct ice_fwlog_module_entry {
|
||||
/* module ID for the corresponding firmware logging event */
|
||||
u16 module_id;
|
||||
/* verbosity level for the module_id */
|
||||
u8 log_level;
|
||||
};
|
||||
|
||||
struct ice_fwlog_cfg {
|
||||
/* list of modules for configuring log level */
|
||||
struct ice_fwlog_module_entry module_entries[ICE_AQC_FW_LOG_ID_MAX];
|
||||
/* options used to configure firmware logging */
|
||||
u16 options;
|
||||
#define ICE_FWLOG_OPTION_ARQ_ENA BIT(0)
|
||||
#define ICE_FWLOG_OPTION_UART_ENA BIT(1)
|
||||
/* set before calling ice_fwlog_init() so the PF registers for firmware
|
||||
* logging on initialization
|
||||
*/
|
||||
#define ICE_FWLOG_OPTION_REGISTER_ON_INIT BIT(2)
|
||||
/* set in the ice_fwlog_get() response if the PF is registered for FW
|
||||
* logging events over ARQ
|
||||
*/
|
||||
#define ICE_FWLOG_OPTION_IS_REGISTERED BIT(3)
|
||||
|
||||
/* minimum number of log events sent per Admin Receive Queue event */
|
||||
u16 log_resolution;
|
||||
};
|
||||
|
||||
struct ice_fwlog_data {
|
||||
u16 data_size;
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
struct ice_fwlog_ring {
|
||||
struct ice_fwlog_data *rings;
|
||||
u16 index;
|
||||
u16 size;
|
||||
u16 head;
|
||||
u16 tail;
|
||||
};
|
||||
|
||||
#define ICE_FWLOG_RING_SIZE_INDEX_DFLT 3
|
||||
#define ICE_FWLOG_RING_SIZE_DFLT 256
|
||||
#define ICE_FWLOG_RING_SIZE_MAX 512
|
||||
|
||||
bool ice_fwlog_ring_full(struct ice_fwlog_ring *rings);
|
||||
bool ice_fwlog_ring_empty(struct ice_fwlog_ring *rings);
|
||||
void ice_fwlog_ring_increment(u16 *item, u16 size);
|
||||
void ice_fwlog_set_supported(struct ice_hw *hw);
|
||||
bool ice_fwlog_supported(struct ice_hw *hw);
|
||||
int ice_fwlog_init(struct ice_hw *hw);
|
||||
void ice_fwlog_deinit(struct ice_hw *hw);
|
||||
int ice_fwlog_set(struct ice_hw *hw, struct ice_fwlog_cfg *cfg);
|
||||
int ice_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg);
|
||||
int ice_fwlog_register(struct ice_hw *hw);
|
||||
int ice_fwlog_unregister(struct ice_hw *hw);
|
||||
void ice_fwlog_realloc_rings(struct ice_hw *hw, int index);
|
||||
#endif /* _ICE_FWLOG_H_ */
|
||||
|
|
@ -39,6 +39,7 @@ static const char ice_copyright[] = "Copyright (c) 2018, Intel Corporation.";
|
|||
MODULE_DESCRIPTION(DRV_SUMMARY);
|
||||
MODULE_IMPORT_NS("LIBIE");
|
||||
MODULE_IMPORT_NS("LIBIE_ADMINQ");
|
||||
MODULE_IMPORT_NS("LIBIE_FWLOG");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_FIRMWARE(ICE_DDP_PKG_FILE);
|
||||
|
||||
|
|
@ -1250,32 +1251,6 @@ 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 ice_fwlog_data *fwlog;
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
|
||||
fwlog = &hw->fwlog_ring.rings[hw->fwlog_ring.tail];
|
||||
|
||||
memset(fwlog->data, 0, PAGE_SIZE);
|
||||
fwlog->data_size = le16_to_cpu(event->desc.datalen);
|
||||
|
||||
memcpy(fwlog->data, event->msg_buf, fwlog->data_size);
|
||||
ice_fwlog_ring_increment(&hw->fwlog_ring.tail, hw->fwlog_ring.size);
|
||||
|
||||
if (ice_fwlog_ring_full(&hw->fwlog_ring)) {
|
||||
/* the rings are full so bump the head to create room */
|
||||
ice_fwlog_ring_increment(&hw->fwlog_ring.head,
|
||||
hw->fwlog_ring.size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_aq_prep_for_event - Prepare to wait for an AdminQ event from firmware
|
||||
* @pf: pointer to the PF private structure
|
||||
|
|
@ -1566,7 +1541,8 @@ static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type)
|
|||
}
|
||||
break;
|
||||
case ice_aqc_opc_fw_logs_event:
|
||||
ice_get_fwlog_data(pf, &event);
|
||||
libie_get_fwlog_data(&hw->fwlog, event.msg_buf,
|
||||
le16_to_cpu(event.desc.datalen));
|
||||
break;
|
||||
case ice_aqc_opc_lldp_set_mib_change:
|
||||
ice_dcb_process_lldp_set_mib_change(pf, &event);
|
||||
|
|
@ -4653,19 +4629,6 @@ static void ice_print_wake_reason(struct ice_pf *pf)
|
|||
dev_info(ice_pf_to_dev(pf), "Wake reason: %s", wake_str);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_pf_fwlog_update_module - update 1 module
|
||||
* @pf: pointer to the PF struct
|
||||
* @log_level: log_level to use for the @module
|
||||
* @module: module to update
|
||||
*/
|
||||
void ice_pf_fwlog_update_module(struct ice_pf *pf, int log_level, int module)
|
||||
{
|
||||
struct ice_hw *hw = &pf->hw;
|
||||
|
||||
hw->fwlog_cfg.module_entries[module].log_level = log_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_register_netdev - register netdev
|
||||
* @vsi: pointer to the VSI struct
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
#include "ice_protocol_type.h"
|
||||
#include "ice_sbq_cmd.h"
|
||||
#include "ice_vlan_mode.h"
|
||||
#include "ice_fwlog.h"
|
||||
#include <linux/net/intel/libie/fwlog.h>
|
||||
#include <linux/wait.h>
|
||||
#include <net/dscp.h>
|
||||
|
||||
|
|
@ -948,9 +948,7 @@ struct ice_hw {
|
|||
u8 fw_patch; /* firmware patch version */
|
||||
u32 fw_build; /* firmware build number */
|
||||
|
||||
struct ice_fwlog_cfg fwlog_cfg;
|
||||
bool fwlog_supported; /* does hardware support FW logging? */
|
||||
struct ice_fwlog_ring fwlog_ring;
|
||||
struct libie_fwlog fwlog;
|
||||
|
||||
/* Device max aggregate bandwidths corresponding to the GL_PWR_MODE_CTL
|
||||
* register. Used for determining the ITR/INTRL granularity during
|
||||
|
|
|
|||
|
|
@ -3921,6 +3921,38 @@ static int ixgbe_read_pba_string_e610(struct ixgbe_hw *hw, u8 *pba_num,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int __fwlog_send_cmd(void *priv, struct libie_aq_desc *desc, void *buf,
|
||||
u16 size)
|
||||
{
|
||||
struct ixgbe_hw *hw = priv;
|
||||
|
||||
return ixgbe_aci_send_cmd(hw, desc, buf, size);
|
||||
}
|
||||
|
||||
int ixgbe_fwlog_init(struct ixgbe_hw *hw)
|
||||
{
|
||||
struct ixgbe_adapter *adapter = hw->back;
|
||||
struct libie_fwlog_api api = {
|
||||
.pdev = adapter->pdev,
|
||||
.send_cmd = __fwlog_send_cmd,
|
||||
.debugfs_root = adapter->ixgbe_dbg_adapter,
|
||||
.priv = hw,
|
||||
};
|
||||
|
||||
if (hw->mac.type != ixgbe_mac_e610)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return libie_fwlog_init(&hw->fwlog, &api);
|
||||
}
|
||||
|
||||
void ixgbe_fwlog_deinit(struct ixgbe_hw *hw)
|
||||
{
|
||||
if (hw->mac.type != ixgbe_mac_e610)
|
||||
return;
|
||||
|
||||
libie_fwlog_deinit(&hw->fwlog);
|
||||
}
|
||||
|
||||
static const struct ixgbe_mac_operations mac_ops_e610 = {
|
||||
.init_hw = ixgbe_init_hw_generic,
|
||||
.start_hw = ixgbe_start_hw_e610,
|
||||
|
|
|
|||
|
|
@ -96,5 +96,7 @@ int ixgbe_aci_update_nvm(struct ixgbe_hw *hw, u16 module_typeid,
|
|||
bool last_command, u8 command_flags);
|
||||
int ixgbe_nvm_write_activate(struct ixgbe_hw *hw, u16 cmd_flags,
|
||||
u8 *response_flags);
|
||||
int ixgbe_fwlog_init(struct ixgbe_hw *hw);
|
||||
void ixgbe_fwlog_deinit(struct ixgbe_hw *hw);
|
||||
|
||||
#endif /* _IXGBE_E610_H_ */
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ static int debug = -1;
|
|||
module_param(debug, int, 0);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
|
||||
|
||||
MODULE_IMPORT_NS("LIBIE_FWLOG");
|
||||
MODULE_DESCRIPTION("Intel(R) 10 Gigabit PCI Express Network Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
|
|
@ -3355,6 +3356,10 @@ static void ixgbe_handle_fw_event(struct ixgbe_adapter *adapter)
|
|||
e_crit(drv, "%s\n", ixgbe_overheat_msg);
|
||||
ixgbe_down(adapter);
|
||||
break;
|
||||
case libie_aqc_opc_fw_logs_event:
|
||||
libie_get_fwlog_data(&hw->fwlog, event.msg_buf,
|
||||
le16_to_cpu(event.desc.datalen));
|
||||
break;
|
||||
default:
|
||||
e_warn(hw, "unknown FW async event captured\n");
|
||||
break;
|
||||
|
|
@ -11998,6 +12003,10 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
ixgbe_devlink_init_regions(adapter);
|
||||
devl_register(adapter->devlink);
|
||||
devl_unlock(adapter->devlink);
|
||||
|
||||
if (ixgbe_fwlog_init(hw))
|
||||
e_dev_info("Firmware logging not supported\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_netdev:
|
||||
|
|
@ -12055,6 +12064,7 @@ static void ixgbe_remove(struct pci_dev *pdev)
|
|||
devl_lock(adapter->devlink);
|
||||
devl_unregister(adapter->devlink);
|
||||
ixgbe_devlink_destroy_regions(adapter);
|
||||
ixgbe_fwlog_deinit(&adapter->hw);
|
||||
ixgbe_dbg_adapter_exit(adapter);
|
||||
|
||||
set_bit(__IXGBE_REMOVING, &adapter->state);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/mdio.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/net/intel/libie/fwlog.h>
|
||||
#include "ixgbe_type_e610.h"
|
||||
|
||||
/* Device IDs */
|
||||
|
|
@ -3752,6 +3753,7 @@ struct ixgbe_hw {
|
|||
struct ixgbe_flash_info flash;
|
||||
struct ixgbe_hw_dev_caps dev_caps;
|
||||
struct ixgbe_hw_func_caps func_caps;
|
||||
struct libie_fwlog fwlog;
|
||||
};
|
||||
|
||||
struct ixgbe_info {
|
||||
|
|
|
|||
|
|
@ -14,3 +14,12 @@ config LIBIE_ADMINQ
|
|||
help
|
||||
Helper functions used by Intel Ethernet drivers for administration
|
||||
queue command interface (aka adminq).
|
||||
|
||||
config LIBIE_FWLOG
|
||||
tristate
|
||||
select LIBIE_ADMINQ
|
||||
help
|
||||
Library to support firmware logging on device that have support
|
||||
for it. Firmware logging is using admin queue interface to communicate
|
||||
with the device. Debugfs is a user interface used to config logging
|
||||
and dump all collected logs.
|
||||
|
|
|
|||
|
|
@ -8,3 +8,7 @@ libie-y := rx.o
|
|||
obj-$(CONFIG_LIBIE_ADMINQ) += libie_adminq.o
|
||||
|
||||
libie_adminq-y := adminq.o
|
||||
|
||||
obj-$(CONFIG_LIBIE_FWLOG) += libie_fwlog.o
|
||||
|
||||
libie_fwlog-y := fwlog.o
|
||||
|
|
|
|||
1115
drivers/net/ethernet/intel/libie/fwlog.c
Normal file
1115
drivers/net/ethernet/intel/libie/fwlog.c
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#define LIBIE_CHECK_STRUCT_LEN(n, X) \
|
||||
static_assert((n) == sizeof(struct X))
|
||||
#define LIBIE_AQ_MAX_BUF_LEN 4096
|
||||
|
||||
/**
|
||||
* struct libie_aqc_generic - Generic structure used in adminq communication
|
||||
|
|
@ -222,6 +223,94 @@ struct libie_aqc_list_caps_elem {
|
|||
};
|
||||
LIBIE_CHECK_STRUCT_LEN(32, libie_aqc_list_caps_elem);
|
||||
|
||||
/* Admin Queue command opcodes */
|
||||
enum libie_adminq_opc {
|
||||
/* FW Logging Commands */
|
||||
libie_aqc_opc_fw_logs_config = 0xFF30,
|
||||
libie_aqc_opc_fw_logs_register = 0xFF31,
|
||||
libie_aqc_opc_fw_logs_query = 0xFF32,
|
||||
libie_aqc_opc_fw_logs_event = 0xFF33,
|
||||
};
|
||||
|
||||
enum libie_aqc_fw_logging_mod {
|
||||
LIBIE_AQC_FW_LOG_ID_GENERAL = 0,
|
||||
LIBIE_AQC_FW_LOG_ID_CTRL,
|
||||
LIBIE_AQC_FW_LOG_ID_LINK,
|
||||
LIBIE_AQC_FW_LOG_ID_LINK_TOPO,
|
||||
LIBIE_AQC_FW_LOG_ID_DNL,
|
||||
LIBIE_AQC_FW_LOG_ID_I2C,
|
||||
LIBIE_AQC_FW_LOG_ID_SDP,
|
||||
LIBIE_AQC_FW_LOG_ID_MDIO,
|
||||
LIBIE_AQC_FW_LOG_ID_ADMINQ,
|
||||
LIBIE_AQC_FW_LOG_ID_HDMA,
|
||||
LIBIE_AQC_FW_LOG_ID_LLDP,
|
||||
LIBIE_AQC_FW_LOG_ID_DCBX,
|
||||
LIBIE_AQC_FW_LOG_ID_DCB,
|
||||
LIBIE_AQC_FW_LOG_ID_XLR,
|
||||
LIBIE_AQC_FW_LOG_ID_NVM,
|
||||
LIBIE_AQC_FW_LOG_ID_AUTH,
|
||||
LIBIE_AQC_FW_LOG_ID_VPD,
|
||||
LIBIE_AQC_FW_LOG_ID_IOSF,
|
||||
LIBIE_AQC_FW_LOG_ID_PARSER,
|
||||
LIBIE_AQC_FW_LOG_ID_SW,
|
||||
LIBIE_AQC_FW_LOG_ID_SCHEDULER,
|
||||
LIBIE_AQC_FW_LOG_ID_TXQ,
|
||||
LIBIE_AQC_FW_LOG_ID_RSVD,
|
||||
LIBIE_AQC_FW_LOG_ID_POST,
|
||||
LIBIE_AQC_FW_LOG_ID_WATCHDOG,
|
||||
LIBIE_AQC_FW_LOG_ID_TASK_DISPATCH,
|
||||
LIBIE_AQC_FW_LOG_ID_MNG,
|
||||
LIBIE_AQC_FW_LOG_ID_SYNCE,
|
||||
LIBIE_AQC_FW_LOG_ID_HEALTH,
|
||||
LIBIE_AQC_FW_LOG_ID_TSDRV,
|
||||
LIBIE_AQC_FW_LOG_ID_PFREG,
|
||||
LIBIE_AQC_FW_LOG_ID_MDLVER,
|
||||
LIBIE_AQC_FW_LOG_ID_MAX
|
||||
};
|
||||
|
||||
/* Set FW Logging configuration (indirect 0xFF30)
|
||||
* Register for FW Logging (indirect 0xFF31)
|
||||
* Query FW Logging (indirect 0xFF32)
|
||||
* FW Log Event (indirect 0xFF33)
|
||||
*/
|
||||
#define LIBIE_AQC_FW_LOG_CONF_UART_EN BIT(0)
|
||||
#define LIBIE_AQC_FW_LOG_CONF_AQ_EN BIT(1)
|
||||
#define LIBIE_AQC_FW_LOG_QUERY_REGISTERED BIT(2)
|
||||
#define LIBIE_AQC_FW_LOG_CONF_SET_VALID BIT(3)
|
||||
#define LIBIE_AQC_FW_LOG_AQ_REGISTER BIT(0)
|
||||
#define LIBIE_AQC_FW_LOG_AQ_QUERY BIT(2)
|
||||
|
||||
#define LIBIE_AQC_FW_LOG_MIN_RESOLUTION 1
|
||||
#define LIBIE_AQC_FW_LOG_MAX_RESOLUTION 128
|
||||
|
||||
struct libie_aqc_fw_log {
|
||||
u8 cmd_flags;
|
||||
|
||||
u8 rsp_flag;
|
||||
__le16 fw_rt_msb;
|
||||
union {
|
||||
struct {
|
||||
__le32 fw_rt_lsb;
|
||||
} sync;
|
||||
struct {
|
||||
__le16 log_resolution;
|
||||
__le16 mdl_cnt;
|
||||
} cfg;
|
||||
} ops;
|
||||
__le32 addr_high;
|
||||
__le32 addr_low;
|
||||
};
|
||||
|
||||
/* Response Buffer for:
|
||||
* Set Firmware Logging Configuration (0xFF30)
|
||||
* Query FW Logging (0xFF32)
|
||||
*/
|
||||
struct libie_aqc_fw_log_cfg_resp {
|
||||
__le16 module_identifier;
|
||||
u8 log_level;
|
||||
u8 rsvd0;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct libie_aq_desc - Admin Queue (AQ) descriptor
|
||||
* @flags: LIBIE_AQ_FLAG_* flags
|
||||
|
|
@ -253,6 +342,7 @@ struct libie_aq_desc {
|
|||
struct libie_aqc_driver_ver driver_ver;
|
||||
struct libie_aqc_req_res res_owner;
|
||||
struct libie_aqc_list_caps get_cap;
|
||||
struct libie_aqc_fw_log fw_log;
|
||||
} params;
|
||||
};
|
||||
LIBIE_CHECK_STRUCT_LEN(32, libie_aq_desc);
|
||||
|
|
|
|||
85
include/linux/net/intel/libie/fwlog.h
Normal file
85
include/linux/net/intel/libie/fwlog.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2022, Intel Corporation. */
|
||||
|
||||
#ifndef _LIBIE_FWLOG_H_
|
||||
#define _LIBIE_FWLOG_H_
|
||||
|
||||
#include <linux/net/intel/libie/adminq.h>
|
||||
|
||||
/* Only a single log level should be set and all log levels under the set value
|
||||
* are enabled, e.g. if log level is set to LIBIE_FW_LOG_LEVEL_VERBOSE, then all
|
||||
* other log levels are included (except LIBIE_FW_LOG_LEVEL_NONE)
|
||||
*/
|
||||
enum libie_fwlog_level {
|
||||
LIBIE_FWLOG_LEVEL_NONE = 0,
|
||||
LIBIE_FWLOG_LEVEL_ERROR = 1,
|
||||
LIBIE_FWLOG_LEVEL_WARNING = 2,
|
||||
LIBIE_FWLOG_LEVEL_NORMAL = 3,
|
||||
LIBIE_FWLOG_LEVEL_VERBOSE = 4,
|
||||
LIBIE_FWLOG_LEVEL_INVALID, /* all values >= this entry are invalid */
|
||||
};
|
||||
|
||||
struct libie_fwlog_module_entry {
|
||||
/* module ID for the corresponding firmware logging event */
|
||||
u16 module_id;
|
||||
/* verbosity level for the module_id */
|
||||
u8 log_level;
|
||||
};
|
||||
|
||||
struct libie_fwlog_cfg {
|
||||
/* list of modules for configuring log level */
|
||||
struct libie_fwlog_module_entry module_entries[LIBIE_AQC_FW_LOG_ID_MAX];
|
||||
/* options used to configure firmware logging */
|
||||
u16 options;
|
||||
#define LIBIE_FWLOG_OPTION_ARQ_ENA BIT(0)
|
||||
#define LIBIE_FWLOG_OPTION_UART_ENA BIT(1)
|
||||
/* set before calling libie_fwlog_init() so the PF registers for
|
||||
* firmware logging on initialization
|
||||
*/
|
||||
#define LIBIE_FWLOG_OPTION_REGISTER_ON_INIT BIT(2)
|
||||
/* set in the libie_aq_fwlog_get() response if the PF is registered for
|
||||
* FW logging events over ARQ
|
||||
*/
|
||||
#define LIBIE_FWLOG_OPTION_IS_REGISTERED BIT(3)
|
||||
|
||||
/* minimum number of log events sent per Admin Receive Queue event */
|
||||
u16 log_resolution;
|
||||
};
|
||||
|
||||
struct libie_fwlog_data {
|
||||
u16 data_size;
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
struct libie_fwlog_ring {
|
||||
struct libie_fwlog_data *rings;
|
||||
u16 index;
|
||||
u16 size;
|
||||
u16 head;
|
||||
u16 tail;
|
||||
};
|
||||
|
||||
#define LIBIE_FWLOG_RING_SIZE_INDEX_DFLT 3
|
||||
#define LIBIE_FWLOG_RING_SIZE_DFLT 256
|
||||
#define LIBIE_FWLOG_RING_SIZE_MAX 512
|
||||
|
||||
struct libie_fwlog {
|
||||
struct libie_fwlog_cfg cfg;
|
||||
bool supported; /* does hardware support FW logging? */
|
||||
struct libie_fwlog_ring ring;
|
||||
struct dentry *debugfs;
|
||||
/* keep track of all the dentrys for FW log modules */
|
||||
struct dentry **debugfs_modules;
|
||||
struct_group_tagged(libie_fwlog_api, api,
|
||||
struct pci_dev *pdev;
|
||||
int (*send_cmd)(void *, struct libie_aq_desc *, void *, u16);
|
||||
void *priv;
|
||||
struct dentry *debugfs_root;
|
||||
);
|
||||
};
|
||||
|
||||
int libie_fwlog_init(struct libie_fwlog *fwlog, struct libie_fwlog_api *api);
|
||||
void libie_fwlog_deinit(struct libie_fwlog *fwlog);
|
||||
void libie_fwlog_reregister(struct libie_fwlog *fwlog);
|
||||
void libie_get_fwlog_data(struct libie_fwlog *fwlog, u8 *buf, u16 len);
|
||||
#endif /* _LIBIE_FWLOG_H_ */
|
||||
Loading…
Reference in New Issue
Block a user