forked from Minki/linux
598ff6bae6
Code is refactored to prepare separate functions for RoCE which can do more complex operations related to reference counting, while still maintainining code readability. This includes (a) Simplification to not perform netdevice checks and modifications for IB link layer. (b) Do not add RoCE GID entry which has NULL netdevice; instead return an error. (c) If GID addition fails at provider level add_gid(), do not add the entry in the cache and keep the entry marked as INVALID. (d) Simplify and reuse the ib_cache_gid_add()/del() routines so that they can be used even for modifying default GIDs. This avoid some code duplication in modifying default GIDs. (e) find_gid() routine refers to the data entry flags to qualify a GID as valid or invalid GID rather than depending on attributes and zeroness of the GID content. (f) gid_table_reserve_default() sets the GID default attribute at beginning while setting up the GID table. There is no need to use default_gid flag in low level functions such as write_gid(), add_gid(), del_gid(), as they never need to update the DEFAULT property of the GID entry while during GID table update. As as result of this refactor, reserved GID 0:0:0:0:0:0:0:0 is no longer searchable as described below. A unicast GID entry of 0:0:0:0:0:0:0:0 is Reserved GID as per the IB spec version 1.3 section 4.1.1, point (6) whose snippet is below. "The unicast GID address 0:0:0:0:0:0:0:0 is reserved - referred to as the Reserved GID. It shall never be assigned to any endport. It shall not be used as a destination address or in a global routing header (GRH)." GID table cache now only stores valid GID entries. Before this patch, Reserved GID 0:0:0:0:0:0:0:0 was searchable in the GID table using ib_find_cached_gid_by_port() and other similar find routines. Zero GID is no longer searchable as it shall not to be present in GRH or path recored entry as described in IB spec version 1.3 section 4.1.1, point (6), section 12.7.10 and section 12.7.20. ib_cache_update() is simplified to check link layer once, use unified locking scheme for all link layers, removed temporary gid table allocation/free logic. Additionally, (a) Expand ib_gid_attr to store port and index so that GID query routines can get port and index information from the attribute structure. (b) Expand ib_gid_attr to store device as well so that in future code when GID reference counting is done, device is used to reach back to the GID table entry. Signed-off-by: Parav Pandit <parav@mellanox.com> Signed-off-by: Leon Romanovsky <leonro@mellanox.com> Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
1381 lines
35 KiB
C
1381 lines
35 KiB
C
/*
|
|
* Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
|
|
* Copyright (c) 2005 Mellanox Technologies Ltd. All rights reserved.
|
|
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
|
|
*
|
|
* This software is available to you under a choice of one of two
|
|
* licenses. You may choose to be licensed under the terms of the GNU
|
|
* General Public License (GPL) Version 2, available from the file
|
|
* COPYING in the main directory of this source tree, or the
|
|
* OpenIB.org BSD license below:
|
|
*
|
|
* Redistribution and use in source and binary forms, with or
|
|
* without modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "core_priv.h"
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/string.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <rdma/ib_mad.h>
|
|
#include <rdma/ib_pma.h>
|
|
|
|
struct ib_port;
|
|
|
|
struct gid_attr_group {
|
|
struct ib_port *port;
|
|
struct kobject kobj;
|
|
struct attribute_group ndev;
|
|
struct attribute_group type;
|
|
};
|
|
struct ib_port {
|
|
struct kobject kobj;
|
|
struct ib_device *ibdev;
|
|
struct gid_attr_group *gid_attr_group;
|
|
struct attribute_group gid_group;
|
|
struct attribute_group pkey_group;
|
|
struct attribute_group *pma_table;
|
|
struct attribute_group *hw_stats_ag;
|
|
struct rdma_hw_stats *hw_stats;
|
|
u8 port_num;
|
|
};
|
|
|
|
struct port_attribute {
|
|
struct attribute attr;
|
|
ssize_t (*show)(struct ib_port *, struct port_attribute *, char *buf);
|
|
ssize_t (*store)(struct ib_port *, struct port_attribute *,
|
|
const char *buf, size_t count);
|
|
};
|
|
|
|
#define PORT_ATTR(_name, _mode, _show, _store) \
|
|
struct port_attribute port_attr_##_name = __ATTR(_name, _mode, _show, _store)
|
|
|
|
#define PORT_ATTR_RO(_name) \
|
|
struct port_attribute port_attr_##_name = __ATTR_RO(_name)
|
|
|
|
struct port_table_attribute {
|
|
struct port_attribute attr;
|
|
char name[8];
|
|
int index;
|
|
__be16 attr_id;
|
|
};
|
|
|
|
struct hw_stats_attribute {
|
|
struct attribute attr;
|
|
ssize_t (*show)(struct kobject *kobj,
|
|
struct attribute *attr, char *buf);
|
|
ssize_t (*store)(struct kobject *kobj,
|
|
struct attribute *attr,
|
|
const char *buf,
|
|
size_t count);
|
|
int index;
|
|
u8 port_num;
|
|
};
|
|
|
|
static ssize_t port_attr_show(struct kobject *kobj,
|
|
struct attribute *attr, char *buf)
|
|
{
|
|
struct port_attribute *port_attr =
|
|
container_of(attr, struct port_attribute, attr);
|
|
struct ib_port *p = container_of(kobj, struct ib_port, kobj);
|
|
|
|
if (!port_attr->show)
|
|
return -EIO;
|
|
|
|
return port_attr->show(p, port_attr, buf);
|
|
}
|
|
|
|
static ssize_t port_attr_store(struct kobject *kobj,
|
|
struct attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct port_attribute *port_attr =
|
|
container_of(attr, struct port_attribute, attr);
|
|
struct ib_port *p = container_of(kobj, struct ib_port, kobj);
|
|
|
|
if (!port_attr->store)
|
|
return -EIO;
|
|
return port_attr->store(p, port_attr, buf, count);
|
|
}
|
|
|
|
static const struct sysfs_ops port_sysfs_ops = {
|
|
.show = port_attr_show,
|
|
.store = port_attr_store
|
|
};
|
|
|
|
static ssize_t gid_attr_show(struct kobject *kobj,
|
|
struct attribute *attr, char *buf)
|
|
{
|
|
struct port_attribute *port_attr =
|
|
container_of(attr, struct port_attribute, attr);
|
|
struct ib_port *p = container_of(kobj, struct gid_attr_group,
|
|
kobj)->port;
|
|
|
|
if (!port_attr->show)
|
|
return -EIO;
|
|
|
|
return port_attr->show(p, port_attr, buf);
|
|
}
|
|
|
|
static const struct sysfs_ops gid_attr_sysfs_ops = {
|
|
.show = gid_attr_show
|
|
};
|
|
|
|
static ssize_t state_show(struct ib_port *p, struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
struct ib_port_attr attr;
|
|
ssize_t ret;
|
|
|
|
static const char *state_name[] = {
|
|
[IB_PORT_NOP] = "NOP",
|
|
[IB_PORT_DOWN] = "DOWN",
|
|
[IB_PORT_INIT] = "INIT",
|
|
[IB_PORT_ARMED] = "ARMED",
|
|
[IB_PORT_ACTIVE] = "ACTIVE",
|
|
[IB_PORT_ACTIVE_DEFER] = "ACTIVE_DEFER"
|
|
};
|
|
|
|
ret = ib_query_port(p->ibdev, p->port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "%d: %s\n", attr.state,
|
|
attr.state >= 0 && attr.state < ARRAY_SIZE(state_name) ?
|
|
state_name[attr.state] : "UNKNOWN");
|
|
}
|
|
|
|
static ssize_t lid_show(struct ib_port *p, struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
struct ib_port_attr attr;
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_port(p->ibdev, p->port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "0x%x\n", attr.lid);
|
|
}
|
|
|
|
static ssize_t lid_mask_count_show(struct ib_port *p,
|
|
struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
struct ib_port_attr attr;
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_port(p->ibdev, p->port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "%d\n", attr.lmc);
|
|
}
|
|
|
|
static ssize_t sm_lid_show(struct ib_port *p, struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
struct ib_port_attr attr;
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_port(p->ibdev, p->port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "0x%x\n", attr.sm_lid);
|
|
}
|
|
|
|
static ssize_t sm_sl_show(struct ib_port *p, struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
struct ib_port_attr attr;
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_port(p->ibdev, p->port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "%d\n", attr.sm_sl);
|
|
}
|
|
|
|
static ssize_t cap_mask_show(struct ib_port *p, struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
struct ib_port_attr attr;
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_port(p->ibdev, p->port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "0x%08x\n", attr.port_cap_flags);
|
|
}
|
|
|
|
static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
struct ib_port_attr attr;
|
|
char *speed = "";
|
|
int rate; /* in deci-Gb/sec */
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_port(p->ibdev, p->port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
switch (attr.active_speed) {
|
|
case IB_SPEED_DDR:
|
|
speed = " DDR";
|
|
rate = 50;
|
|
break;
|
|
case IB_SPEED_QDR:
|
|
speed = " QDR";
|
|
rate = 100;
|
|
break;
|
|
case IB_SPEED_FDR10:
|
|
speed = " FDR10";
|
|
rate = 100;
|
|
break;
|
|
case IB_SPEED_FDR:
|
|
speed = " FDR";
|
|
rate = 140;
|
|
break;
|
|
case IB_SPEED_EDR:
|
|
speed = " EDR";
|
|
rate = 250;
|
|
break;
|
|
case IB_SPEED_HDR:
|
|
speed = " HDR";
|
|
rate = 500;
|
|
break;
|
|
case IB_SPEED_SDR:
|
|
default: /* default to SDR for invalid rates */
|
|
speed = " SDR";
|
|
rate = 25;
|
|
break;
|
|
}
|
|
|
|
rate *= ib_width_enum_to_int(attr.active_width);
|
|
if (rate < 0)
|
|
return -EINVAL;
|
|
|
|
return sprintf(buf, "%d%s Gb/sec (%dX%s)\n",
|
|
rate / 10, rate % 10 ? ".5" : "",
|
|
ib_width_enum_to_int(attr.active_width), speed);
|
|
}
|
|
|
|
static ssize_t phys_state_show(struct ib_port *p, struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
struct ib_port_attr attr;
|
|
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_port(p->ibdev, p->port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
switch (attr.phys_state) {
|
|
case 1: return sprintf(buf, "1: Sleep\n");
|
|
case 2: return sprintf(buf, "2: Polling\n");
|
|
case 3: return sprintf(buf, "3: Disabled\n");
|
|
case 4: return sprintf(buf, "4: PortConfigurationTraining\n");
|
|
case 5: return sprintf(buf, "5: LinkUp\n");
|
|
case 6: return sprintf(buf, "6: LinkErrorRecovery\n");
|
|
case 7: return sprintf(buf, "7: Phy Test\n");
|
|
default: return sprintf(buf, "%d: <unknown>\n", attr.phys_state);
|
|
}
|
|
}
|
|
|
|
static ssize_t link_layer_show(struct ib_port *p, struct port_attribute *unused,
|
|
char *buf)
|
|
{
|
|
switch (rdma_port_get_link_layer(p->ibdev, p->port_num)) {
|
|
case IB_LINK_LAYER_INFINIBAND:
|
|
return sprintf(buf, "%s\n", "InfiniBand");
|
|
case IB_LINK_LAYER_ETHERNET:
|
|
return sprintf(buf, "%s\n", "Ethernet");
|
|
default:
|
|
return sprintf(buf, "%s\n", "Unknown");
|
|
}
|
|
}
|
|
|
|
static PORT_ATTR_RO(state);
|
|
static PORT_ATTR_RO(lid);
|
|
static PORT_ATTR_RO(lid_mask_count);
|
|
static PORT_ATTR_RO(sm_lid);
|
|
static PORT_ATTR_RO(sm_sl);
|
|
static PORT_ATTR_RO(cap_mask);
|
|
static PORT_ATTR_RO(rate);
|
|
static PORT_ATTR_RO(phys_state);
|
|
static PORT_ATTR_RO(link_layer);
|
|
|
|
static struct attribute *port_default_attrs[] = {
|
|
&port_attr_state.attr,
|
|
&port_attr_lid.attr,
|
|
&port_attr_lid_mask_count.attr,
|
|
&port_attr_sm_lid.attr,
|
|
&port_attr_sm_sl.attr,
|
|
&port_attr_cap_mask.attr,
|
|
&port_attr_rate.attr,
|
|
&port_attr_phys_state.attr,
|
|
&port_attr_link_layer.attr,
|
|
NULL
|
|
};
|
|
|
|
static size_t print_ndev(struct ib_gid_attr *gid_attr, char *buf)
|
|
{
|
|
if (!gid_attr->ndev)
|
|
return -EINVAL;
|
|
|
|
return sprintf(buf, "%s\n", gid_attr->ndev->name);
|
|
}
|
|
|
|
static size_t print_gid_type(struct ib_gid_attr *gid_attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%s\n", ib_cache_gid_type_str(gid_attr->gid_type));
|
|
}
|
|
|
|
static ssize_t _show_port_gid_attr(struct ib_port *p,
|
|
struct port_attribute *attr,
|
|
char *buf,
|
|
size_t (*print)(struct ib_gid_attr *gid_attr,
|
|
char *buf))
|
|
{
|
|
struct port_table_attribute *tab_attr =
|
|
container_of(attr, struct port_table_attribute, attr);
|
|
union ib_gid gid;
|
|
struct ib_gid_attr gid_attr = {};
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_gid(p->ibdev, p->port_num, tab_attr->index, &gid,
|
|
&gid_attr);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = print(&gid_attr, buf);
|
|
|
|
err:
|
|
if (gid_attr.ndev)
|
|
dev_put(gid_attr.ndev);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t show_port_gid(struct ib_port *p, struct port_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct port_table_attribute *tab_attr =
|
|
container_of(attr, struct port_table_attribute, attr);
|
|
union ib_gid *pgid;
|
|
union ib_gid gid;
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_gid(p->ibdev, p->port_num, tab_attr->index, &gid, NULL);
|
|
|
|
/* If reading GID fails, it is likely due to GID entry being empty
|
|
* (invalid) or reserved GID in the table.
|
|
* User space expects to read GID table entries as long as it given
|
|
* index is within GID table size.
|
|
* Administrative/debugging tool fails to query rest of the GID entries
|
|
* if it hits error while querying a GID of the given index.
|
|
* To avoid user space throwing such error on fail to read gid, return
|
|
* zero GID as before. This maintains backward compatibility.
|
|
*/
|
|
if (ret)
|
|
pgid = &zgid;
|
|
else
|
|
pgid = &gid;
|
|
return sprintf(buf, "%pI6\n", pgid->raw);
|
|
}
|
|
|
|
static ssize_t show_port_gid_attr_ndev(struct ib_port *p,
|
|
struct port_attribute *attr, char *buf)
|
|
{
|
|
return _show_port_gid_attr(p, attr, buf, print_ndev);
|
|
}
|
|
|
|
static ssize_t show_port_gid_attr_gid_type(struct ib_port *p,
|
|
struct port_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return _show_port_gid_attr(p, attr, buf, print_gid_type);
|
|
}
|
|
|
|
static ssize_t show_port_pkey(struct ib_port *p, struct port_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct port_table_attribute *tab_attr =
|
|
container_of(attr, struct port_table_attribute, attr);
|
|
u16 pkey;
|
|
ssize_t ret;
|
|
|
|
ret = ib_query_pkey(p->ibdev, p->port_num, tab_attr->index, &pkey);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "0x%04x\n", pkey);
|
|
}
|
|
|
|
#define PORT_PMA_ATTR(_name, _counter, _width, _offset) \
|
|
struct port_table_attribute port_pma_attr_##_name = { \
|
|
.attr = __ATTR(_name, S_IRUGO, show_pma_counter, NULL), \
|
|
.index = (_offset) | ((_width) << 16) | ((_counter) << 24), \
|
|
.attr_id = IB_PMA_PORT_COUNTERS , \
|
|
}
|
|
|
|
#define PORT_PMA_ATTR_EXT(_name, _width, _offset) \
|
|
struct port_table_attribute port_pma_attr_ext_##_name = { \
|
|
.attr = __ATTR(_name, S_IRUGO, show_pma_counter, NULL), \
|
|
.index = (_offset) | ((_width) << 16), \
|
|
.attr_id = IB_PMA_PORT_COUNTERS_EXT , \
|
|
}
|
|
|
|
/*
|
|
* Get a Perfmgmt MAD block of data.
|
|
* Returns error code or the number of bytes retrieved.
|
|
*/
|
|
static int get_perf_mad(struct ib_device *dev, int port_num, __be16 attr,
|
|
void *data, int offset, size_t size)
|
|
{
|
|
struct ib_mad *in_mad;
|
|
struct ib_mad *out_mad;
|
|
size_t mad_size = sizeof(*out_mad);
|
|
u16 out_mad_pkey_index = 0;
|
|
ssize_t ret;
|
|
|
|
if (!dev->process_mad)
|
|
return -ENOSYS;
|
|
|
|
in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
|
|
out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
|
|
if (!in_mad || !out_mad) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
in_mad->mad_hdr.base_version = 1;
|
|
in_mad->mad_hdr.mgmt_class = IB_MGMT_CLASS_PERF_MGMT;
|
|
in_mad->mad_hdr.class_version = 1;
|
|
in_mad->mad_hdr.method = IB_MGMT_METHOD_GET;
|
|
in_mad->mad_hdr.attr_id = attr;
|
|
|
|
if (attr != IB_PMA_CLASS_PORT_INFO)
|
|
in_mad->data[41] = port_num; /* PortSelect field */
|
|
|
|
if ((dev->process_mad(dev, IB_MAD_IGNORE_MKEY,
|
|
port_num, NULL, NULL,
|
|
(const struct ib_mad_hdr *)in_mad, mad_size,
|
|
(struct ib_mad_hdr *)out_mad, &mad_size,
|
|
&out_mad_pkey_index) &
|
|
(IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) !=
|
|
(IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
memcpy(data, out_mad->data + offset, size);
|
|
ret = size;
|
|
out:
|
|
kfree(in_mad);
|
|
kfree(out_mad);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t show_pma_counter(struct ib_port *p, struct port_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct port_table_attribute *tab_attr =
|
|
container_of(attr, struct port_table_attribute, attr);
|
|
int offset = tab_attr->index & 0xffff;
|
|
int width = (tab_attr->index >> 16) & 0xff;
|
|
ssize_t ret;
|
|
u8 data[8];
|
|
|
|
ret = get_perf_mad(p->ibdev, p->port_num, tab_attr->attr_id, &data,
|
|
40 + offset / 8, sizeof(data));
|
|
if (ret < 0)
|
|
return sprintf(buf, "N/A (no PMA)\n");
|
|
|
|
switch (width) {
|
|
case 4:
|
|
ret = sprintf(buf, "%u\n", (*data >>
|
|
(4 - (offset % 8))) & 0xf);
|
|
break;
|
|
case 8:
|
|
ret = sprintf(buf, "%u\n", *data);
|
|
break;
|
|
case 16:
|
|
ret = sprintf(buf, "%u\n",
|
|
be16_to_cpup((__be16 *)data));
|
|
break;
|
|
case 32:
|
|
ret = sprintf(buf, "%u\n",
|
|
be32_to_cpup((__be32 *)data));
|
|
break;
|
|
case 64:
|
|
ret = sprintf(buf, "%llu\n",
|
|
be64_to_cpup((__be64 *)data));
|
|
break;
|
|
|
|
default:
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static PORT_PMA_ATTR(symbol_error , 0, 16, 32);
|
|
static PORT_PMA_ATTR(link_error_recovery , 1, 8, 48);
|
|
static PORT_PMA_ATTR(link_downed , 2, 8, 56);
|
|
static PORT_PMA_ATTR(port_rcv_errors , 3, 16, 64);
|
|
static PORT_PMA_ATTR(port_rcv_remote_physical_errors, 4, 16, 80);
|
|
static PORT_PMA_ATTR(port_rcv_switch_relay_errors , 5, 16, 96);
|
|
static PORT_PMA_ATTR(port_xmit_discards , 6, 16, 112);
|
|
static PORT_PMA_ATTR(port_xmit_constraint_errors , 7, 8, 128);
|
|
static PORT_PMA_ATTR(port_rcv_constraint_errors , 8, 8, 136);
|
|
static PORT_PMA_ATTR(local_link_integrity_errors , 9, 4, 152);
|
|
static PORT_PMA_ATTR(excessive_buffer_overrun_errors, 10, 4, 156);
|
|
static PORT_PMA_ATTR(VL15_dropped , 11, 16, 176);
|
|
static PORT_PMA_ATTR(port_xmit_data , 12, 32, 192);
|
|
static PORT_PMA_ATTR(port_rcv_data , 13, 32, 224);
|
|
static PORT_PMA_ATTR(port_xmit_packets , 14, 32, 256);
|
|
static PORT_PMA_ATTR(port_rcv_packets , 15, 32, 288);
|
|
static PORT_PMA_ATTR(port_xmit_wait , 0, 32, 320);
|
|
|
|
/*
|
|
* Counters added by extended set
|
|
*/
|
|
static PORT_PMA_ATTR_EXT(port_xmit_data , 64, 64);
|
|
static PORT_PMA_ATTR_EXT(port_rcv_data , 64, 128);
|
|
static PORT_PMA_ATTR_EXT(port_xmit_packets , 64, 192);
|
|
static PORT_PMA_ATTR_EXT(port_rcv_packets , 64, 256);
|
|
static PORT_PMA_ATTR_EXT(unicast_xmit_packets , 64, 320);
|
|
static PORT_PMA_ATTR_EXT(unicast_rcv_packets , 64, 384);
|
|
static PORT_PMA_ATTR_EXT(multicast_xmit_packets , 64, 448);
|
|
static PORT_PMA_ATTR_EXT(multicast_rcv_packets , 64, 512);
|
|
|
|
static struct attribute *pma_attrs[] = {
|
|
&port_pma_attr_symbol_error.attr.attr,
|
|
&port_pma_attr_link_error_recovery.attr.attr,
|
|
&port_pma_attr_link_downed.attr.attr,
|
|
&port_pma_attr_port_rcv_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_remote_physical_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_switch_relay_errors.attr.attr,
|
|
&port_pma_attr_port_xmit_discards.attr.attr,
|
|
&port_pma_attr_port_xmit_constraint_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_constraint_errors.attr.attr,
|
|
&port_pma_attr_local_link_integrity_errors.attr.attr,
|
|
&port_pma_attr_excessive_buffer_overrun_errors.attr.attr,
|
|
&port_pma_attr_VL15_dropped.attr.attr,
|
|
&port_pma_attr_port_xmit_data.attr.attr,
|
|
&port_pma_attr_port_rcv_data.attr.attr,
|
|
&port_pma_attr_port_xmit_packets.attr.attr,
|
|
&port_pma_attr_port_rcv_packets.attr.attr,
|
|
&port_pma_attr_port_xmit_wait.attr.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *pma_attrs_ext[] = {
|
|
&port_pma_attr_symbol_error.attr.attr,
|
|
&port_pma_attr_link_error_recovery.attr.attr,
|
|
&port_pma_attr_link_downed.attr.attr,
|
|
&port_pma_attr_port_rcv_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_remote_physical_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_switch_relay_errors.attr.attr,
|
|
&port_pma_attr_port_xmit_discards.attr.attr,
|
|
&port_pma_attr_port_xmit_constraint_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_constraint_errors.attr.attr,
|
|
&port_pma_attr_local_link_integrity_errors.attr.attr,
|
|
&port_pma_attr_excessive_buffer_overrun_errors.attr.attr,
|
|
&port_pma_attr_VL15_dropped.attr.attr,
|
|
&port_pma_attr_ext_port_xmit_data.attr.attr,
|
|
&port_pma_attr_ext_port_rcv_data.attr.attr,
|
|
&port_pma_attr_ext_port_xmit_packets.attr.attr,
|
|
&port_pma_attr_port_xmit_wait.attr.attr,
|
|
&port_pma_attr_ext_port_rcv_packets.attr.attr,
|
|
&port_pma_attr_ext_unicast_rcv_packets.attr.attr,
|
|
&port_pma_attr_ext_unicast_xmit_packets.attr.attr,
|
|
&port_pma_attr_ext_multicast_rcv_packets.attr.attr,
|
|
&port_pma_attr_ext_multicast_xmit_packets.attr.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *pma_attrs_noietf[] = {
|
|
&port_pma_attr_symbol_error.attr.attr,
|
|
&port_pma_attr_link_error_recovery.attr.attr,
|
|
&port_pma_attr_link_downed.attr.attr,
|
|
&port_pma_attr_port_rcv_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_remote_physical_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_switch_relay_errors.attr.attr,
|
|
&port_pma_attr_port_xmit_discards.attr.attr,
|
|
&port_pma_attr_port_xmit_constraint_errors.attr.attr,
|
|
&port_pma_attr_port_rcv_constraint_errors.attr.attr,
|
|
&port_pma_attr_local_link_integrity_errors.attr.attr,
|
|
&port_pma_attr_excessive_buffer_overrun_errors.attr.attr,
|
|
&port_pma_attr_VL15_dropped.attr.attr,
|
|
&port_pma_attr_ext_port_xmit_data.attr.attr,
|
|
&port_pma_attr_ext_port_rcv_data.attr.attr,
|
|
&port_pma_attr_ext_port_xmit_packets.attr.attr,
|
|
&port_pma_attr_ext_port_rcv_packets.attr.attr,
|
|
&port_pma_attr_port_xmit_wait.attr.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group pma_group = {
|
|
.name = "counters",
|
|
.attrs = pma_attrs
|
|
};
|
|
|
|
static struct attribute_group pma_group_ext = {
|
|
.name = "counters",
|
|
.attrs = pma_attrs_ext
|
|
};
|
|
|
|
static struct attribute_group pma_group_noietf = {
|
|
.name = "counters",
|
|
.attrs = pma_attrs_noietf
|
|
};
|
|
|
|
static void ib_port_release(struct kobject *kobj)
|
|
{
|
|
struct ib_port *p = container_of(kobj, struct ib_port, kobj);
|
|
struct attribute *a;
|
|
int i;
|
|
|
|
if (p->gid_group.attrs) {
|
|
for (i = 0; (a = p->gid_group.attrs[i]); ++i)
|
|
kfree(a);
|
|
|
|
kfree(p->gid_group.attrs);
|
|
}
|
|
|
|
if (p->pkey_group.attrs) {
|
|
for (i = 0; (a = p->pkey_group.attrs[i]); ++i)
|
|
kfree(a);
|
|
|
|
kfree(p->pkey_group.attrs);
|
|
}
|
|
|
|
kfree(p);
|
|
}
|
|
|
|
static void ib_port_gid_attr_release(struct kobject *kobj)
|
|
{
|
|
struct gid_attr_group *g = container_of(kobj, struct gid_attr_group,
|
|
kobj);
|
|
struct attribute *a;
|
|
int i;
|
|
|
|
if (g->ndev.attrs) {
|
|
for (i = 0; (a = g->ndev.attrs[i]); ++i)
|
|
kfree(a);
|
|
|
|
kfree(g->ndev.attrs);
|
|
}
|
|
|
|
if (g->type.attrs) {
|
|
for (i = 0; (a = g->type.attrs[i]); ++i)
|
|
kfree(a);
|
|
|
|
kfree(g->type.attrs);
|
|
}
|
|
|
|
kfree(g);
|
|
}
|
|
|
|
static struct kobj_type port_type = {
|
|
.release = ib_port_release,
|
|
.sysfs_ops = &port_sysfs_ops,
|
|
.default_attrs = port_default_attrs
|
|
};
|
|
|
|
static struct kobj_type gid_attr_type = {
|
|
.sysfs_ops = &gid_attr_sysfs_ops,
|
|
.release = ib_port_gid_attr_release
|
|
};
|
|
|
|
static struct attribute **
|
|
alloc_group_attrs(ssize_t (*show)(struct ib_port *,
|
|
struct port_attribute *, char *buf),
|
|
int len)
|
|
{
|
|
struct attribute **tab_attr;
|
|
struct port_table_attribute *element;
|
|
int i;
|
|
|
|
tab_attr = kcalloc(1 + len, sizeof(struct attribute *), GFP_KERNEL);
|
|
if (!tab_attr)
|
|
return NULL;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
element = kzalloc(sizeof(struct port_table_attribute),
|
|
GFP_KERNEL);
|
|
if (!element)
|
|
goto err;
|
|
|
|
if (snprintf(element->name, sizeof(element->name),
|
|
"%d", i) >= sizeof(element->name)) {
|
|
kfree(element);
|
|
goto err;
|
|
}
|
|
|
|
element->attr.attr.name = element->name;
|
|
element->attr.attr.mode = S_IRUGO;
|
|
element->attr.show = show;
|
|
element->index = i;
|
|
sysfs_attr_init(&element->attr.attr);
|
|
|
|
tab_attr[i] = &element->attr.attr;
|
|
}
|
|
|
|
return tab_attr;
|
|
|
|
err:
|
|
while (--i >= 0)
|
|
kfree(tab_attr[i]);
|
|
kfree(tab_attr);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Figure out which counter table to use depending on
|
|
* the device capabilities.
|
|
*/
|
|
static struct attribute_group *get_counter_table(struct ib_device *dev,
|
|
int port_num)
|
|
{
|
|
struct ib_class_port_info cpi;
|
|
|
|
if (get_perf_mad(dev, port_num, IB_PMA_CLASS_PORT_INFO,
|
|
&cpi, 40, sizeof(cpi)) >= 0) {
|
|
if (cpi.capability_mask & IB_PMA_CLASS_CAP_EXT_WIDTH)
|
|
/* We have extended counters */
|
|
return &pma_group_ext;
|
|
|
|
if (cpi.capability_mask & IB_PMA_CLASS_CAP_EXT_WIDTH_NOIETF)
|
|
/* But not the IETF ones */
|
|
return &pma_group_noietf;
|
|
}
|
|
|
|
/* Fall back to normal counters */
|
|
return &pma_group;
|
|
}
|
|
|
|
static int update_hw_stats(struct ib_device *dev, struct rdma_hw_stats *stats,
|
|
u8 port_num, int index)
|
|
{
|
|
int ret;
|
|
|
|
if (time_is_after_eq_jiffies(stats->timestamp + stats->lifespan))
|
|
return 0;
|
|
ret = dev->get_hw_stats(dev, stats, port_num, index);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (ret == stats->num_counters)
|
|
stats->timestamp = jiffies;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t print_hw_stat(struct rdma_hw_stats *stats, int index, char *buf)
|
|
{
|
|
return sprintf(buf, "%llu\n", stats->value[index]);
|
|
}
|
|
|
|
static ssize_t show_hw_stats(struct kobject *kobj, struct attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct ib_device *dev;
|
|
struct ib_port *port;
|
|
struct hw_stats_attribute *hsa;
|
|
struct rdma_hw_stats *stats;
|
|
int ret;
|
|
|
|
hsa = container_of(attr, struct hw_stats_attribute, attr);
|
|
if (!hsa->port_num) {
|
|
dev = container_of((struct device *)kobj,
|
|
struct ib_device, dev);
|
|
stats = dev->hw_stats;
|
|
} else {
|
|
port = container_of(kobj, struct ib_port, kobj);
|
|
dev = port->ibdev;
|
|
stats = port->hw_stats;
|
|
}
|
|
mutex_lock(&stats->lock);
|
|
ret = update_hw_stats(dev, stats, hsa->port_num, hsa->index);
|
|
if (ret)
|
|
goto unlock;
|
|
ret = print_hw_stat(stats, hsa->index, buf);
|
|
unlock:
|
|
mutex_unlock(&stats->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t show_stats_lifespan(struct kobject *kobj,
|
|
struct attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct hw_stats_attribute *hsa;
|
|
struct rdma_hw_stats *stats;
|
|
int msecs;
|
|
|
|
hsa = container_of(attr, struct hw_stats_attribute, attr);
|
|
if (!hsa->port_num) {
|
|
struct ib_device *dev = container_of((struct device *)kobj,
|
|
struct ib_device, dev);
|
|
|
|
stats = dev->hw_stats;
|
|
} else {
|
|
struct ib_port *p = container_of(kobj, struct ib_port, kobj);
|
|
|
|
stats = p->hw_stats;
|
|
}
|
|
|
|
mutex_lock(&stats->lock);
|
|
msecs = jiffies_to_msecs(stats->lifespan);
|
|
mutex_unlock(&stats->lock);
|
|
|
|
return sprintf(buf, "%d\n", msecs);
|
|
}
|
|
|
|
static ssize_t set_stats_lifespan(struct kobject *kobj,
|
|
struct attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct hw_stats_attribute *hsa;
|
|
struct rdma_hw_stats *stats;
|
|
int msecs;
|
|
int jiffies;
|
|
int ret;
|
|
|
|
ret = kstrtoint(buf, 10, &msecs);
|
|
if (ret)
|
|
return ret;
|
|
if (msecs < 0 || msecs > 10000)
|
|
return -EINVAL;
|
|
jiffies = msecs_to_jiffies(msecs);
|
|
hsa = container_of(attr, struct hw_stats_attribute, attr);
|
|
if (!hsa->port_num) {
|
|
struct ib_device *dev = container_of((struct device *)kobj,
|
|
struct ib_device, dev);
|
|
|
|
stats = dev->hw_stats;
|
|
} else {
|
|
struct ib_port *p = container_of(kobj, struct ib_port, kobj);
|
|
|
|
stats = p->hw_stats;
|
|
}
|
|
|
|
mutex_lock(&stats->lock);
|
|
stats->lifespan = jiffies;
|
|
mutex_unlock(&stats->lock);
|
|
|
|
return count;
|
|
}
|
|
|
|
static void free_hsag(struct kobject *kobj, struct attribute_group *attr_group)
|
|
{
|
|
struct attribute **attr;
|
|
|
|
sysfs_remove_group(kobj, attr_group);
|
|
|
|
for (attr = attr_group->attrs; *attr; attr++)
|
|
kfree(*attr);
|
|
kfree(attr_group);
|
|
}
|
|
|
|
static struct attribute *alloc_hsa(int index, u8 port_num, const char *name)
|
|
{
|
|
struct hw_stats_attribute *hsa;
|
|
|
|
hsa = kmalloc(sizeof(*hsa), GFP_KERNEL);
|
|
if (!hsa)
|
|
return NULL;
|
|
|
|
hsa->attr.name = (char *)name;
|
|
hsa->attr.mode = S_IRUGO;
|
|
hsa->show = show_hw_stats;
|
|
hsa->store = NULL;
|
|
hsa->index = index;
|
|
hsa->port_num = port_num;
|
|
|
|
return &hsa->attr;
|
|
}
|
|
|
|
static struct attribute *alloc_hsa_lifespan(char *name, u8 port_num)
|
|
{
|
|
struct hw_stats_attribute *hsa;
|
|
|
|
hsa = kmalloc(sizeof(*hsa), GFP_KERNEL);
|
|
if (!hsa)
|
|
return NULL;
|
|
|
|
hsa->attr.name = name;
|
|
hsa->attr.mode = S_IWUSR | S_IRUGO;
|
|
hsa->show = show_stats_lifespan;
|
|
hsa->store = set_stats_lifespan;
|
|
hsa->index = 0;
|
|
hsa->port_num = port_num;
|
|
|
|
return &hsa->attr;
|
|
}
|
|
|
|
static void setup_hw_stats(struct ib_device *device, struct ib_port *port,
|
|
u8 port_num)
|
|
{
|
|
struct attribute_group *hsag;
|
|
struct rdma_hw_stats *stats;
|
|
int i, ret;
|
|
|
|
stats = device->alloc_hw_stats(device, port_num);
|
|
|
|
if (!stats)
|
|
return;
|
|
|
|
if (!stats->names || stats->num_counters <= 0)
|
|
goto err_free_stats;
|
|
|
|
/*
|
|
* Two extra attribue elements here, one for the lifespan entry and
|
|
* one to NULL terminate the list for the sysfs core code
|
|
*/
|
|
hsag = kzalloc(sizeof(*hsag) +
|
|
sizeof(void *) * (stats->num_counters + 2),
|
|
GFP_KERNEL);
|
|
if (!hsag)
|
|
goto err_free_stats;
|
|
|
|
ret = device->get_hw_stats(device, stats, port_num,
|
|
stats->num_counters);
|
|
if (ret != stats->num_counters)
|
|
goto err_free_hsag;
|
|
|
|
stats->timestamp = jiffies;
|
|
|
|
hsag->name = "hw_counters";
|
|
hsag->attrs = (void *)hsag + sizeof(*hsag);
|
|
|
|
for (i = 0; i < stats->num_counters; i++) {
|
|
hsag->attrs[i] = alloc_hsa(i, port_num, stats->names[i]);
|
|
if (!hsag->attrs[i])
|
|
goto err;
|
|
sysfs_attr_init(hsag->attrs[i]);
|
|
}
|
|
|
|
mutex_init(&stats->lock);
|
|
/* treat an error here as non-fatal */
|
|
hsag->attrs[i] = alloc_hsa_lifespan("lifespan", port_num);
|
|
if (hsag->attrs[i])
|
|
sysfs_attr_init(hsag->attrs[i]);
|
|
|
|
if (port) {
|
|
struct kobject *kobj = &port->kobj;
|
|
ret = sysfs_create_group(kobj, hsag);
|
|
if (ret)
|
|
goto err;
|
|
port->hw_stats_ag = hsag;
|
|
port->hw_stats = stats;
|
|
} else {
|
|
struct kobject *kobj = &device->dev.kobj;
|
|
ret = sysfs_create_group(kobj, hsag);
|
|
if (ret)
|
|
goto err;
|
|
device->hw_stats_ag = hsag;
|
|
device->hw_stats = stats;
|
|
}
|
|
|
|
return;
|
|
|
|
err:
|
|
for (; i >= 0; i--)
|
|
kfree(hsag->attrs[i]);
|
|
err_free_hsag:
|
|
kfree(hsag);
|
|
err_free_stats:
|
|
kfree(stats);
|
|
return;
|
|
}
|
|
|
|
static int add_port(struct ib_device *device, int port_num,
|
|
int (*port_callback)(struct ib_device *,
|
|
u8, struct kobject *))
|
|
{
|
|
struct ib_port *p;
|
|
struct ib_port_attr attr;
|
|
int i;
|
|
int ret;
|
|
|
|
ret = ib_query_port(device, port_num, &attr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
p = kzalloc(sizeof *p, GFP_KERNEL);
|
|
if (!p)
|
|
return -ENOMEM;
|
|
|
|
p->ibdev = device;
|
|
p->port_num = port_num;
|
|
|
|
ret = kobject_init_and_add(&p->kobj, &port_type,
|
|
device->ports_parent,
|
|
"%d", port_num);
|
|
if (ret) {
|
|
kfree(p);
|
|
return ret;
|
|
}
|
|
|
|
p->gid_attr_group = kzalloc(sizeof(*p->gid_attr_group), GFP_KERNEL);
|
|
if (!p->gid_attr_group) {
|
|
ret = -ENOMEM;
|
|
goto err_put;
|
|
}
|
|
|
|
p->gid_attr_group->port = p;
|
|
ret = kobject_init_and_add(&p->gid_attr_group->kobj, &gid_attr_type,
|
|
&p->kobj, "gid_attrs");
|
|
if (ret) {
|
|
kfree(p->gid_attr_group);
|
|
goto err_put;
|
|
}
|
|
|
|
p->pma_table = get_counter_table(device, port_num);
|
|
ret = sysfs_create_group(&p->kobj, p->pma_table);
|
|
if (ret)
|
|
goto err_put_gid_attrs;
|
|
|
|
p->gid_group.name = "gids";
|
|
p->gid_group.attrs = alloc_group_attrs(show_port_gid, attr.gid_tbl_len);
|
|
if (!p->gid_group.attrs) {
|
|
ret = -ENOMEM;
|
|
goto err_remove_pma;
|
|
}
|
|
|
|
ret = sysfs_create_group(&p->kobj, &p->gid_group);
|
|
if (ret)
|
|
goto err_free_gid;
|
|
|
|
p->gid_attr_group->ndev.name = "ndevs";
|
|
p->gid_attr_group->ndev.attrs = alloc_group_attrs(show_port_gid_attr_ndev,
|
|
attr.gid_tbl_len);
|
|
if (!p->gid_attr_group->ndev.attrs) {
|
|
ret = -ENOMEM;
|
|
goto err_remove_gid;
|
|
}
|
|
|
|
ret = sysfs_create_group(&p->gid_attr_group->kobj,
|
|
&p->gid_attr_group->ndev);
|
|
if (ret)
|
|
goto err_free_gid_ndev;
|
|
|
|
p->gid_attr_group->type.name = "types";
|
|
p->gid_attr_group->type.attrs = alloc_group_attrs(show_port_gid_attr_gid_type,
|
|
attr.gid_tbl_len);
|
|
if (!p->gid_attr_group->type.attrs) {
|
|
ret = -ENOMEM;
|
|
goto err_remove_gid_ndev;
|
|
}
|
|
|
|
ret = sysfs_create_group(&p->gid_attr_group->kobj,
|
|
&p->gid_attr_group->type);
|
|
if (ret)
|
|
goto err_free_gid_type;
|
|
|
|
p->pkey_group.name = "pkeys";
|
|
p->pkey_group.attrs = alloc_group_attrs(show_port_pkey,
|
|
attr.pkey_tbl_len);
|
|
if (!p->pkey_group.attrs) {
|
|
ret = -ENOMEM;
|
|
goto err_remove_gid_type;
|
|
}
|
|
|
|
ret = sysfs_create_group(&p->kobj, &p->pkey_group);
|
|
if (ret)
|
|
goto err_free_pkey;
|
|
|
|
if (port_callback) {
|
|
ret = port_callback(device, port_num, &p->kobj);
|
|
if (ret)
|
|
goto err_remove_pkey;
|
|
}
|
|
|
|
/*
|
|
* If port == 0, it means we have only one port and the parent
|
|
* device, not this port device, should be the holder of the
|
|
* hw_counters
|
|
*/
|
|
if (device->alloc_hw_stats && port_num)
|
|
setup_hw_stats(device, p, port_num);
|
|
|
|
list_add_tail(&p->kobj.entry, &device->port_list);
|
|
|
|
kobject_uevent(&p->kobj, KOBJ_ADD);
|
|
return 0;
|
|
|
|
err_remove_pkey:
|
|
sysfs_remove_group(&p->kobj, &p->pkey_group);
|
|
|
|
err_free_pkey:
|
|
for (i = 0; i < attr.pkey_tbl_len; ++i)
|
|
kfree(p->pkey_group.attrs[i]);
|
|
|
|
kfree(p->pkey_group.attrs);
|
|
p->pkey_group.attrs = NULL;
|
|
|
|
err_remove_gid_type:
|
|
sysfs_remove_group(&p->gid_attr_group->kobj,
|
|
&p->gid_attr_group->type);
|
|
|
|
err_free_gid_type:
|
|
for (i = 0; i < attr.gid_tbl_len; ++i)
|
|
kfree(p->gid_attr_group->type.attrs[i]);
|
|
|
|
kfree(p->gid_attr_group->type.attrs);
|
|
p->gid_attr_group->type.attrs = NULL;
|
|
|
|
err_remove_gid_ndev:
|
|
sysfs_remove_group(&p->gid_attr_group->kobj,
|
|
&p->gid_attr_group->ndev);
|
|
|
|
err_free_gid_ndev:
|
|
for (i = 0; i < attr.gid_tbl_len; ++i)
|
|
kfree(p->gid_attr_group->ndev.attrs[i]);
|
|
|
|
kfree(p->gid_attr_group->ndev.attrs);
|
|
p->gid_attr_group->ndev.attrs = NULL;
|
|
|
|
err_remove_gid:
|
|
sysfs_remove_group(&p->kobj, &p->gid_group);
|
|
|
|
err_free_gid:
|
|
for (i = 0; i < attr.gid_tbl_len; ++i)
|
|
kfree(p->gid_group.attrs[i]);
|
|
|
|
kfree(p->gid_group.attrs);
|
|
p->gid_group.attrs = NULL;
|
|
|
|
err_remove_pma:
|
|
sysfs_remove_group(&p->kobj, p->pma_table);
|
|
|
|
err_put_gid_attrs:
|
|
kobject_put(&p->gid_attr_group->kobj);
|
|
|
|
err_put:
|
|
kobject_put(&p->kobj);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t show_node_type(struct device *device,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ib_device *dev = container_of(device, struct ib_device, dev);
|
|
|
|
switch (dev->node_type) {
|
|
case RDMA_NODE_IB_CA: return sprintf(buf, "%d: CA\n", dev->node_type);
|
|
case RDMA_NODE_RNIC: return sprintf(buf, "%d: RNIC\n", dev->node_type);
|
|
case RDMA_NODE_USNIC: return sprintf(buf, "%d: usNIC\n", dev->node_type);
|
|
case RDMA_NODE_USNIC_UDP: return sprintf(buf, "%d: usNIC UDP\n", dev->node_type);
|
|
case RDMA_NODE_IB_SWITCH: return sprintf(buf, "%d: switch\n", dev->node_type);
|
|
case RDMA_NODE_IB_ROUTER: return sprintf(buf, "%d: router\n", dev->node_type);
|
|
default: return sprintf(buf, "%d: <unknown>\n", dev->node_type);
|
|
}
|
|
}
|
|
|
|
static ssize_t show_sys_image_guid(struct device *device,
|
|
struct device_attribute *dev_attr, char *buf)
|
|
{
|
|
struct ib_device *dev = container_of(device, struct ib_device, dev);
|
|
|
|
return sprintf(buf, "%04x:%04x:%04x:%04x\n",
|
|
be16_to_cpu(((__be16 *) &dev->attrs.sys_image_guid)[0]),
|
|
be16_to_cpu(((__be16 *) &dev->attrs.sys_image_guid)[1]),
|
|
be16_to_cpu(((__be16 *) &dev->attrs.sys_image_guid)[2]),
|
|
be16_to_cpu(((__be16 *) &dev->attrs.sys_image_guid)[3]));
|
|
}
|
|
|
|
static ssize_t show_node_guid(struct device *device,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ib_device *dev = container_of(device, struct ib_device, dev);
|
|
|
|
return sprintf(buf, "%04x:%04x:%04x:%04x\n",
|
|
be16_to_cpu(((__be16 *) &dev->node_guid)[0]),
|
|
be16_to_cpu(((__be16 *) &dev->node_guid)[1]),
|
|
be16_to_cpu(((__be16 *) &dev->node_guid)[2]),
|
|
be16_to_cpu(((__be16 *) &dev->node_guid)[3]));
|
|
}
|
|
|
|
static ssize_t show_node_desc(struct device *device,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ib_device *dev = container_of(device, struct ib_device, dev);
|
|
|
|
return sprintf(buf, "%.64s\n", dev->node_desc);
|
|
}
|
|
|
|
static ssize_t set_node_desc(struct device *device,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct ib_device *dev = container_of(device, struct ib_device, dev);
|
|
struct ib_device_modify desc = {};
|
|
int ret;
|
|
|
|
if (!dev->modify_device)
|
|
return -EIO;
|
|
|
|
memcpy(desc.node_desc, buf, min_t(int, count, IB_DEVICE_NODE_DESC_MAX));
|
|
ret = ib_modify_device(dev, IB_DEVICE_MODIFY_NODE_DESC, &desc);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct ib_device *dev = container_of(device, struct ib_device, dev);
|
|
|
|
ib_get_device_fw_str(dev, buf);
|
|
strlcat(buf, "\n", IB_FW_VERSION_NAME_MAX);
|
|
return strlen(buf);
|
|
}
|
|
|
|
static DEVICE_ATTR(node_type, S_IRUGO, show_node_type, NULL);
|
|
static DEVICE_ATTR(sys_image_guid, S_IRUGO, show_sys_image_guid, NULL);
|
|
static DEVICE_ATTR(node_guid, S_IRUGO, show_node_guid, NULL);
|
|
static DEVICE_ATTR(node_desc, S_IRUGO | S_IWUSR, show_node_desc, set_node_desc);
|
|
static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
|
|
|
|
static struct device_attribute *ib_class_attributes[] = {
|
|
&dev_attr_node_type,
|
|
&dev_attr_sys_image_guid,
|
|
&dev_attr_node_guid,
|
|
&dev_attr_node_desc,
|
|
&dev_attr_fw_ver,
|
|
};
|
|
|
|
static void free_port_list_attributes(struct ib_device *device)
|
|
{
|
|
struct kobject *p, *t;
|
|
|
|
list_for_each_entry_safe(p, t, &device->port_list, entry) {
|
|
struct ib_port *port = container_of(p, struct ib_port, kobj);
|
|
list_del(&p->entry);
|
|
if (port->hw_stats) {
|
|
kfree(port->hw_stats);
|
|
free_hsag(&port->kobj, port->hw_stats_ag);
|
|
}
|
|
sysfs_remove_group(p, port->pma_table);
|
|
sysfs_remove_group(p, &port->pkey_group);
|
|
sysfs_remove_group(p, &port->gid_group);
|
|
sysfs_remove_group(&port->gid_attr_group->kobj,
|
|
&port->gid_attr_group->ndev);
|
|
sysfs_remove_group(&port->gid_attr_group->kobj,
|
|
&port->gid_attr_group->type);
|
|
kobject_put(&port->gid_attr_group->kobj);
|
|
kobject_put(p);
|
|
}
|
|
|
|
kobject_put(device->ports_parent);
|
|
}
|
|
|
|
int ib_device_register_sysfs(struct ib_device *device,
|
|
int (*port_callback)(struct ib_device *,
|
|
u8, struct kobject *))
|
|
{
|
|
struct device *class_dev = &device->dev;
|
|
int ret;
|
|
int i;
|
|
|
|
ret = dev_set_name(class_dev, "%s", device->name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = device_add(class_dev);
|
|
if (ret)
|
|
goto err;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ib_class_attributes); ++i) {
|
|
ret = device_create_file(class_dev, ib_class_attributes[i]);
|
|
if (ret)
|
|
goto err_unregister;
|
|
}
|
|
|
|
device->ports_parent = kobject_create_and_add("ports",
|
|
&class_dev->kobj);
|
|
if (!device->ports_parent) {
|
|
ret = -ENOMEM;
|
|
goto err_put;
|
|
}
|
|
|
|
if (rdma_cap_ib_switch(device)) {
|
|
ret = add_port(device, 0, port_callback);
|
|
if (ret)
|
|
goto err_put;
|
|
} else {
|
|
for (i = 1; i <= device->phys_port_cnt; ++i) {
|
|
ret = add_port(device, i, port_callback);
|
|
if (ret)
|
|
goto err_put;
|
|
}
|
|
}
|
|
|
|
if (device->alloc_hw_stats)
|
|
setup_hw_stats(device, NULL, 0);
|
|
|
|
return 0;
|
|
|
|
err_put:
|
|
free_port_list_attributes(device);
|
|
|
|
err_unregister:
|
|
device_del(class_dev);
|
|
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
void ib_device_unregister_sysfs(struct ib_device *device)
|
|
{
|
|
int i;
|
|
|
|
/* Hold kobject until ib_dealloc_device() */
|
|
kobject_get(&device->dev.kobj);
|
|
|
|
free_port_list_attributes(device);
|
|
|
|
if (device->hw_stats) {
|
|
kfree(device->hw_stats);
|
|
free_hsag(&device->dev.kobj, device->hw_stats_ag);
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ib_class_attributes); ++i)
|
|
device_remove_file(&device->dev, ib_class_attributes[i]);
|
|
|
|
device_unregister(&device->dev);
|
|
}
|