From b3c687317ff5d65017ee431de9ccf80ec1552e3e Mon Sep 17 00:00:00 2001 From: Anirban Chakraborty Date: Thu, 12 May 2011 12:48:34 +0000 Subject: qlcnic: Take FW dump via ethtool 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 Signed-off-by: David S. Miller --- drivers/net/qlcnic/qlcnic_ethtool.c | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'drivers/net/qlcnic') diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index c541461bc12c..9efc690a289f 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c @@ -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, @@ -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, }; -- cgit v1.2.3