Skip to content

Commit

Permalink
qlcnic: add eswitch statistics support
Browse files Browse the repository at this point in the history
Adding eswitch statistics support. User can get
whole eswitch stats or stats of func belong to that eswitch.

Added:
o command to get statistics of eswitch and function.
o sysfs support to export eswitch and func statatistics.

Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Amit Kumar Salecha authored and David S. Miller committed Aug 17, 2010
1 parent ecd7d31 commit b602121
Show file tree
Hide file tree
Showing 3 changed files with 287 additions and 1 deletion.
31 changes: 31 additions & 0 deletions drivers/net/qlcnic/qlcnic.h
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ struct qlcnic_recv_context {
#define QLCNIC_CDRP_CMD_GET_ESWITCH_STATUS 0x00000026
#define QLCNIC_CDRP_CMD_SET_PORTMIRRORING 0x00000027
#define QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH 0x00000028
#define QLCNIC_CDRP_CMD_GET_ESWITCH_STATS 0x0000002a

#define QLCNIC_RCODE_SUCCESS 0
#define QLCNIC_RCODE_TIMEOUT 17
Expand Down Expand Up @@ -1126,6 +1127,31 @@ struct qlcnic_esw_func_cfg {
u8 reserved;
};

#define QLCNIC_STATS_VERSION 1
#define QLCNIC_STATS_PORT 1
#define QLCNIC_STATS_ESWITCH 2
#define QLCNIC_QUERY_RX_COUNTER 0
#define QLCNIC_QUERY_TX_COUNTER 1
struct __qlcnic_esw_statistics {
__le16 context_id;
__le16 version;
__le16 size;
__le16 unused;
__le64 unicast_frames;
__le64 multicast_frames;
__le64 broadcast_frames;
__le64 dropped_frames;
__le64 errors;
__le64 local_frames;
__le64 numbytes;
__le64 rsvd[3];
};

struct qlcnic_esw_statistics {
struct __qlcnic_esw_statistics rx;
struct __qlcnic_esw_statistics tx;
};

int qlcnic_fw_cmd_query_phy(struct qlcnic_adapter *adapter, u32 reg, u32 *val);
int qlcnic_fw_cmd_set_phy(struct qlcnic_adapter *adapter, u32 reg, u32 val);

Expand Down Expand Up @@ -1252,6 +1278,11 @@ int qlcnic_toggle_eswitch(struct qlcnic_adapter *, u8, u8);
int qlcnic_config_switch_port(struct qlcnic_adapter *, u8, int, u8, u8,
u8, u8, u16);
int qlcnic_config_port_mirroring(struct qlcnic_adapter *, u8, u8, u8);
int qlcnic_get_port_stats(struct qlcnic_adapter *, const u8, const u8,
struct __qlcnic_esw_statistics *);
int qlcnic_get_eswitch_stats(struct qlcnic_adapter *, const u8, u8,
struct __qlcnic_esw_statistics *);
int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, u8, u8, u8);
extern int qlcnic_config_tso;

/*
Expand Down
125 changes: 125 additions & 0 deletions drivers/net/qlcnic/qlcnic_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -983,3 +983,128 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, u8 id,

return err;
}

int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
const u8 rx_tx, struct __qlcnic_esw_statistics *esw_stats) {

size_t stats_size = sizeof(struct __qlcnic_esw_statistics);
dma_addr_t stats_dma_t;
void *stats_addr;
u32 arg1;
int err;

if (esw_stats == NULL)
return -ENOMEM;

if (adapter->op_mode != QLCNIC_MGMT_FUNC &&
func != adapter->ahw.pci_func) {
dev_err(&adapter->pdev->dev,
"Not privilege to query stats for func=%d", func);
return -EIO;
}

stats_addr = pci_alloc_consistent(adapter->pdev, stats_size,
&stats_dma_t);
if (!stats_addr) {
dev_err(&adapter->pdev->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
memset(stats_addr, 0, stats_size);

arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12;
arg1 |= rx_tx << 15 | stats_size << 16;

err = qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
adapter->fw_hal_version,
arg1,
MSD(stats_dma_t),
LSD(stats_dma_t),
QLCNIC_CDRP_CMD_GET_ESWITCH_STATS);

if (!err)
memcpy(esw_stats, stats_addr, stats_size);

pci_free_consistent(adapter->pdev, stats_size, stats_addr,
stats_dma_t);
return err;
}

int qlcnic_get_eswitch_stats(struct qlcnic_adapter *adapter, const u8 eswitch,
const u8 rx_tx, struct __qlcnic_esw_statistics *esw_stats) {

struct __qlcnic_esw_statistics port_stats;
u8 i;
int ret = -EIO;

if (esw_stats == NULL)
return -ENOMEM;
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
return -EIO;
if (adapter->npars == NULL)
return -EIO;

memset(esw_stats, 0, sizeof(struct __qlcnic_esw_statistics));
esw_stats->context_id = eswitch;

for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
if (adapter->npars[i].phy_port != eswitch)
continue;

memset(&port_stats, 0, sizeof(struct __qlcnic_esw_statistics));
if (qlcnic_get_port_stats(adapter, i, rx_tx, &port_stats))
continue;

esw_stats->size = port_stats.size;
esw_stats->version = port_stats.version;
esw_stats->unicast_frames += port_stats.unicast_frames;
esw_stats->multicast_frames += port_stats.multicast_frames;
esw_stats->broadcast_frames += port_stats.broadcast_frames;
esw_stats->dropped_frames += port_stats.dropped_frames;
esw_stats->errors += port_stats.errors;
esw_stats->local_frames += port_stats.local_frames;
esw_stats->numbytes += port_stats.numbytes;

ret = 0;
}
return ret;
}

int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw,
const u8 port, const u8 rx_tx)
{

u32 arg1;

if (adapter->op_mode != QLCNIC_MGMT_FUNC)
return -EIO;

if (func_esw == QLCNIC_STATS_PORT) {
if (port >= QLCNIC_MAX_PCI_FUNC)
goto err_ret;
} else if (func_esw == QLCNIC_STATS_ESWITCH) {
if (port >= QLCNIC_NIU_MAX_XG_PORTS)
goto err_ret;
} else {
goto err_ret;
}

if (rx_tx > QLCNIC_QUERY_TX_COUNTER)
goto err_ret;

arg1 = port | QLCNIC_STATS_VERSION << 8 | func_esw << 12;
arg1 |= BIT_14 | rx_tx << 15;

return qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
adapter->fw_hal_version,
arg1,
0,
0,
QLCNIC_CDRP_CMD_GET_ESWITCH_STATS);

err_ret:
dev_err(&adapter->pdev->dev, "Invalid argument func_esw=%d port=%d"
"rx_ctx=%d\n", func_esw, port, rx_tx);
return -EIO;
}
132 changes: 131 additions & 1 deletion drivers/net/qlcnic/qlcnic_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3377,6 +3377,115 @@ qlcnic_sysfs_read_npar_config(struct file *file, struct kobject *kobj,
return size;
}

static ssize_t
qlcnic_sysfs_get_port_stats(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
struct qlcnic_esw_statistics port_stats;
int ret;

if (size != sizeof(struct qlcnic_esw_statistics))
return QL_STATUS_INVALID_PARAM;

if (offset >= QLCNIC_MAX_PCI_FUNC)
return QL_STATUS_INVALID_PARAM;

memset(&port_stats, 0, size);
ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER,
&port_stats.rx);
if (ret)
return ret;

ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER,
&port_stats.tx);
if (ret)
return ret;

memcpy(buf, &port_stats, size);
return size;
}

static ssize_t
qlcnic_sysfs_get_esw_stats(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
struct qlcnic_esw_statistics esw_stats;
int ret;

if (size != sizeof(struct qlcnic_esw_statistics))
return QL_STATUS_INVALID_PARAM;

if (offset >= QLCNIC_NIU_MAX_XG_PORTS)
return QL_STATUS_INVALID_PARAM;

memset(&esw_stats, 0, size);
ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER,
&esw_stats.rx);
if (ret)
return ret;

ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER,
&esw_stats.tx);
if (ret)
return ret;

memcpy(buf, &esw_stats, size);
return size;
}

static ssize_t
qlcnic_sysfs_clear_esw_stats(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
int ret;

if (offset >= QLCNIC_NIU_MAX_XG_PORTS)
return QL_STATUS_INVALID_PARAM;

ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset,
QLCNIC_QUERY_RX_COUNTER);
if (ret)
return ret;

ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset,
QLCNIC_QUERY_TX_COUNTER);
if (ret)
return ret;

return size;
}

static ssize_t
qlcnic_sysfs_clear_port_stats(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
{

struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
int ret;

if (offset >= QLCNIC_MAX_PCI_FUNC)
return QL_STATUS_INVALID_PARAM;

ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset,
QLCNIC_QUERY_RX_COUNTER);
if (ret)
return ret;

ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset,
QLCNIC_QUERY_TX_COUNTER);
if (ret)
return ret;

return size;
}

static ssize_t
qlcnic_sysfs_read_pci_config(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t offset, size_t size)
Expand Down Expand Up @@ -3426,6 +3535,20 @@ static struct bin_attribute bin_attr_pci_config = {
.write = NULL,
};

static struct bin_attribute bin_attr_port_stats = {
.attr = {.name = "port_stats", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_get_port_stats,
.write = qlcnic_sysfs_clear_port_stats,
};

static struct bin_attribute bin_attr_esw_stats = {
.attr = {.name = "esw_stats", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_get_esw_stats,
.write = qlcnic_sysfs_clear_esw_stats,
};

static struct bin_attribute bin_attr_esw_config = {
.attr = {.name = "esw_config", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
Expand Down Expand Up @@ -3465,6 +3588,9 @@ qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
{
struct device *dev = &adapter->pdev->dev;

if (device_create_bin_file(dev, &bin_attr_port_stats))
dev_info(dev, "failed to create port stats sysfs entry");

if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC)
return;
if (device_create_file(dev, &dev_attr_diag_mode))
Expand All @@ -3484,14 +3610,17 @@ qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
dev_info(dev, "failed to create esw config sysfs entry");
if (device_create_bin_file(dev, &bin_attr_pm_config))
dev_info(dev, "failed to create pm config sysfs entry");

if (device_create_bin_file(dev, &bin_attr_esw_stats))
dev_info(dev, "failed to create eswitch stats sysfs entry");
}

static void
qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
{
struct device *dev = &adapter->pdev->dev;

device_remove_bin_file(dev, &bin_attr_port_stats);

if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC)
return;
device_remove_file(dev, &dev_attr_diag_mode);
Expand All @@ -3504,6 +3633,7 @@ qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
device_remove_bin_file(dev, &bin_attr_npar_config);
device_remove_bin_file(dev, &bin_attr_esw_config);
device_remove_bin_file(dev, &bin_attr_pm_config);
device_remove_bin_file(dev, &bin_attr_esw_stats);
}

#ifdef CONFIG_INET
Expand Down

0 comments on commit b602121

Please sign in to comment.