Skip to content

Commit

Permalink
qlcnic: Take FW dump via ethtool
Browse files Browse the repository at this point in the history
Driver checks if the previous dump has been cleared before taking the dump.
It doesn't take the dump if it is not cleared.

Changes from v2:
Added lock to protect dump data structures from being mangled while
dumping or setting them via ethtool.

Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Anirban Chakraborty authored and David S. Miller committed May 13, 2011
1 parent 18f2f61 commit b3c6873
Showing 1 changed file with 81 additions and 0 deletions.
81 changes: 81 additions & 0 deletions drivers/net/qlcnic/qlcnic_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,84 @@ static void qlcnic_set_msglevel(struct net_device *netdev, u32 msglvl)
adapter->msg_enable = msglvl;
}

static int
qlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;

dump->len = fw_dump->tmpl_hdr->size + fw_dump->size;
dump->flag = fw_dump->tmpl_hdr->drv_cap_mask;
dump->version = adapter->fw_version;
return 0;
}

static int
qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
void *buffer)
{
int i, copy_sz;
u32 *hdr_ptr, *data;
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;

if (qlcnic_api_lock(adapter))
return -EIO;
if (!fw_dump->clr) {
netdev_info(netdev, "Dump not available\n");
qlcnic_api_unlock(adapter);
return -EINVAL;
}
/* Copy template header first */
copy_sz = fw_dump->tmpl_hdr->size;
hdr_ptr = (u32 *) fw_dump->tmpl_hdr;
data = (u32 *) buffer;
for (i = 0; i < copy_sz/sizeof(u32); i++)
*data++ = cpu_to_le32(*hdr_ptr++);

/* Copy captured dump data */
memcpy(buffer + copy_sz, fw_dump->data, fw_dump->size);
dump->len = copy_sz + fw_dump->size;
dump->flag = fw_dump->tmpl_hdr->drv_cap_mask;

/* Free dump area once data has been captured */
vfree(fw_dump->data);
fw_dump->data = NULL;
fw_dump->clr = 0;
qlcnic_api_unlock(adapter);

return 0;
}

static int
qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
{
int ret = 0;
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;

if (val->flag == QLCNIC_FORCE_FW_DUMP_KEY) {
netdev_info(netdev, "Forcing a FW dump\n");
qlcnic_dev_request_reset(adapter);
} else {
if (val->flag > QLCNIC_DUMP_MASK_MAX ||
val->flag < QLCNIC_DUMP_MASK_MIN) {
netdev_info(netdev,
"Invalid dump level: 0x%x\n", val->flag);
ret = -EINVAL;
goto out;
}
if (qlcnic_api_lock(adapter))
return -EIO;
fw_dump->tmpl_hdr->drv_cap_mask = val->flag & 0xff;
qlcnic_api_unlock(adapter);
netdev_info(netdev, "Driver mask changed to: 0x%x\n",
fw_dump->tmpl_hdr->drv_cap_mask);
}
out:
return ret;
}

const struct ethtool_ops qlcnic_ethtool_ops = {
.get_settings = qlcnic_get_settings,
.set_settings = qlcnic_set_settings,
Expand All @@ -991,4 +1069,7 @@ const struct ethtool_ops qlcnic_ethtool_ops = {
.set_phys_id = qlcnic_set_led,
.set_msglevel = qlcnic_set_msglevel,
.get_msglevel = qlcnic_get_msglevel,
.get_dump_flag = qlcnic_get_dump_flag,
.get_dump_data = qlcnic_get_dump_data,
.set_dump = qlcnic_set_dump,
};

0 comments on commit b3c6873

Please sign in to comment.