1/* 2 * Copyright (c) 2013, Cisco Systems, Inc. All rights reserved. 3 * 4 * This program is free software; you may redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; version 2 of the License. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 9 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 10 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 11 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 12 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 13 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 15 * SOFTWARE. 16 * 17 */ 18 19#include <linux/module.h> 20#include <linux/init.h> 21#include <linux/errno.h> 22 23#include <rdma/ib_user_verbs.h> 24#include <rdma/ib_addr.h> 25 26#include "usnic_common_util.h" 27#include "usnic_ib.h" 28#include "usnic_ib_qp_grp.h" 29#include "usnic_vnic.h" 30#include "usnic_ib_verbs.h" 31#include "usnic_log.h" 32 33static ssize_t usnic_ib_show_fw_ver(struct device *device, 34 struct device_attribute *attr, 35 char *buf) 36{ 37 struct usnic_ib_dev *us_ibdev = 38 container_of(device, struct usnic_ib_dev, ib_dev.dev); 39 struct ethtool_drvinfo info; 40 41 mutex_lock(&us_ibdev->usdev_lock); 42 us_ibdev->netdev->ethtool_ops->get_drvinfo(us_ibdev->netdev, &info); 43 mutex_unlock(&us_ibdev->usdev_lock); 44 45 return scnprintf(buf, PAGE_SIZE, "%s\n", info.fw_version); 46} 47 48static ssize_t usnic_ib_show_board(struct device *device, 49 struct device_attribute *attr, 50 char *buf) 51{ 52 struct usnic_ib_dev *us_ibdev = 53 container_of(device, struct usnic_ib_dev, ib_dev.dev); 54 unsigned short subsystem_device_id; 55 56 mutex_lock(&us_ibdev->usdev_lock); 57 subsystem_device_id = us_ibdev->pdev->subsystem_device; 58 mutex_unlock(&us_ibdev->usdev_lock); 59 60 return scnprintf(buf, PAGE_SIZE, "%hu\n", subsystem_device_id); 61} 62 63/* 64 * Report the configuration for this PF 65 */ 66static ssize_t 67usnic_ib_show_config(struct device *device, struct device_attribute *attr, 68 char *buf) 69{ 70 struct usnic_ib_dev *us_ibdev; 71 char *ptr; 72 unsigned left; 73 unsigned n; 74 enum usnic_vnic_res_type res_type; 75 76 us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); 77 78 /* Buffer space limit is 1 page */ 79 ptr = buf; 80 left = PAGE_SIZE; 81 82 mutex_lock(&us_ibdev->usdev_lock); 83 if (atomic_read(&us_ibdev->vf_cnt.refcount) > 0) { 84 char *busname; 85 86 /* 87 * bus name seems to come with annoying prefix. 88 * Remove it if it is predictable 89 */ 90 busname = us_ibdev->pdev->bus->name; 91 if (strncmp(busname, "PCI Bus ", 8) == 0) 92 busname += 8; 93 94 n = scnprintf(ptr, left, 95 "%s: %s:%d.%d, %s, %pM, %u VFs\n Per VF:", 96 us_ibdev->ib_dev.name, 97 busname, 98 PCI_SLOT(us_ibdev->pdev->devfn), 99 PCI_FUNC(us_ibdev->pdev->devfn), 100 netdev_name(us_ibdev->netdev), 101 us_ibdev->ufdev->mac, 102 atomic_read(&us_ibdev->vf_cnt.refcount)); 103 UPDATE_PTR_LEFT(n, ptr, left); 104 105 for (res_type = USNIC_VNIC_RES_TYPE_EOL; 106 res_type < USNIC_VNIC_RES_TYPE_MAX; 107 res_type++) { 108 if (us_ibdev->vf_res_cnt[res_type] == 0) 109 continue; 110 n = scnprintf(ptr, left, " %d %s%s", 111 us_ibdev->vf_res_cnt[res_type], 112 usnic_vnic_res_type_to_str(res_type), 113 (res_type < (USNIC_VNIC_RES_TYPE_MAX - 1)) ? 114 "," : ""); 115 UPDATE_PTR_LEFT(n, ptr, left); 116 } 117 n = scnprintf(ptr, left, "\n"); 118 UPDATE_PTR_LEFT(n, ptr, left); 119 } else { 120 n = scnprintf(ptr, left, "%s: no VFs\n", 121 us_ibdev->ib_dev.name); 122 UPDATE_PTR_LEFT(n, ptr, left); 123 } 124 mutex_unlock(&us_ibdev->usdev_lock); 125 126 return ptr - buf; 127} 128 129static ssize_t 130usnic_ib_show_iface(struct device *device, struct device_attribute *attr, 131 char *buf) 132{ 133 struct usnic_ib_dev *us_ibdev; 134 135 us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); 136 137 return scnprintf(buf, PAGE_SIZE, "%s\n", 138 netdev_name(us_ibdev->netdev)); 139} 140 141static ssize_t 142usnic_ib_show_max_vf(struct device *device, struct device_attribute *attr, 143 char *buf) 144{ 145 struct usnic_ib_dev *us_ibdev; 146 147 us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); 148 149 return scnprintf(buf, PAGE_SIZE, "%u\n", 150 atomic_read(&us_ibdev->vf_cnt.refcount)); 151} 152 153static ssize_t 154usnic_ib_show_qp_per_vf(struct device *device, struct device_attribute *attr, 155 char *buf) 156{ 157 struct usnic_ib_dev *us_ibdev; 158 int qp_per_vf; 159 160 us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); 161 qp_per_vf = max(us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_WQ], 162 us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_RQ]); 163 164 return scnprintf(buf, PAGE_SIZE, 165 "%d\n", qp_per_vf); 166} 167 168static ssize_t 169usnic_ib_show_cq_per_vf(struct device *device, struct device_attribute *attr, 170 char *buf) 171{ 172 struct usnic_ib_dev *us_ibdev; 173 174 us_ibdev = container_of(device, struct usnic_ib_dev, ib_dev.dev); 175 176 return scnprintf(buf, PAGE_SIZE, "%d\n", 177 us_ibdev->vf_res_cnt[USNIC_VNIC_RES_TYPE_CQ]); 178} 179 180static DEVICE_ATTR(fw_ver, S_IRUGO, usnic_ib_show_fw_ver, NULL); 181static DEVICE_ATTR(board_id, S_IRUGO, usnic_ib_show_board, NULL); 182static DEVICE_ATTR(config, S_IRUGO, usnic_ib_show_config, NULL); 183static DEVICE_ATTR(iface, S_IRUGO, usnic_ib_show_iface, NULL); 184static DEVICE_ATTR(max_vf, S_IRUGO, usnic_ib_show_max_vf, NULL); 185static DEVICE_ATTR(qp_per_vf, S_IRUGO, usnic_ib_show_qp_per_vf, NULL); 186static DEVICE_ATTR(cq_per_vf, S_IRUGO, usnic_ib_show_cq_per_vf, NULL); 187 188static struct device_attribute *usnic_class_attributes[] = { 189 &dev_attr_fw_ver, 190 &dev_attr_board_id, 191 &dev_attr_config, 192 &dev_attr_iface, 193 &dev_attr_max_vf, 194 &dev_attr_qp_per_vf, 195 &dev_attr_cq_per_vf, 196}; 197 198struct qpn_attribute { 199 struct attribute attr; 200 ssize_t (*show)(struct usnic_ib_qp_grp *, char *buf); 201}; 202 203/* 204 * Definitions for supporting QPN entries in sysfs 205 */ 206static ssize_t 207usnic_ib_qpn_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) 208{ 209 struct usnic_ib_qp_grp *qp_grp; 210 struct qpn_attribute *qpn_attr; 211 212 qp_grp = container_of(kobj, struct usnic_ib_qp_grp, kobj); 213 qpn_attr = container_of(attr, struct qpn_attribute, attr); 214 215 return qpn_attr->show(qp_grp, buf); 216} 217 218static const struct sysfs_ops usnic_ib_qpn_sysfs_ops = { 219 .show = usnic_ib_qpn_attr_show 220}; 221 222#define QPN_ATTR_RO(NAME) \ 223struct qpn_attribute qpn_attr_##NAME = __ATTR_RO(NAME) 224 225static ssize_t context_show(struct usnic_ib_qp_grp *qp_grp, char *buf) 226{ 227 return scnprintf(buf, PAGE_SIZE, "0x%p\n", qp_grp->ctx); 228} 229 230static ssize_t summary_show(struct usnic_ib_qp_grp *qp_grp, char *buf) 231{ 232 int i, j, n; 233 int left; 234 char *ptr; 235 struct usnic_vnic_res_chunk *res_chunk; 236 struct usnic_vnic_res *vnic_res; 237 238 left = PAGE_SIZE; 239 ptr = buf; 240 241 n = scnprintf(ptr, left, 242 "QPN: %d State: (%s) PID: %u VF Idx: %hu ", 243 qp_grp->ibqp.qp_num, 244 usnic_ib_qp_grp_state_to_string(qp_grp->state), 245 qp_grp->owner_pid, 246 usnic_vnic_get_index(qp_grp->vf->vnic)); 247 UPDATE_PTR_LEFT(n, ptr, left); 248 249 for (i = 0; qp_grp->res_chunk_list[i]; i++) { 250 res_chunk = qp_grp->res_chunk_list[i]; 251 for (j = 0; j < res_chunk->cnt; j++) { 252 vnic_res = res_chunk->res[j]; 253 n = scnprintf(ptr, left, "%s[%d] ", 254 usnic_vnic_res_type_to_str(vnic_res->type), 255 vnic_res->vnic_idx); 256 UPDATE_PTR_LEFT(n, ptr, left); 257 } 258 } 259 260 n = scnprintf(ptr, left, "\n"); 261 UPDATE_PTR_LEFT(n, ptr, left); 262 263 return ptr - buf; 264} 265 266static QPN_ATTR_RO(context); 267static QPN_ATTR_RO(summary); 268 269static struct attribute *usnic_ib_qpn_default_attrs[] = { 270 &qpn_attr_context.attr, 271 &qpn_attr_summary.attr, 272 NULL 273}; 274 275static struct kobj_type usnic_ib_qpn_type = { 276 .sysfs_ops = &usnic_ib_qpn_sysfs_ops, 277 .default_attrs = usnic_ib_qpn_default_attrs 278}; 279 280int usnic_ib_sysfs_register_usdev(struct usnic_ib_dev *us_ibdev) 281{ 282 int i; 283 int err; 284 for (i = 0; i < ARRAY_SIZE(usnic_class_attributes); ++i) { 285 err = device_create_file(&us_ibdev->ib_dev.dev, 286 usnic_class_attributes[i]); 287 if (err) { 288 usnic_err("Failed to create device file %d for %s eith err %d", 289 i, us_ibdev->ib_dev.name, err); 290 return -EINVAL; 291 } 292 } 293 294 /* create kernel object for looking at individual QPs */ 295 kobject_get(&us_ibdev->ib_dev.dev.kobj); 296 us_ibdev->qpn_kobj = kobject_create_and_add("qpn", 297 &us_ibdev->ib_dev.dev.kobj); 298 if (us_ibdev->qpn_kobj == NULL) { 299 kobject_put(&us_ibdev->ib_dev.dev.kobj); 300 return -ENOMEM; 301 } 302 303 return 0; 304} 305 306void usnic_ib_sysfs_unregister_usdev(struct usnic_ib_dev *us_ibdev) 307{ 308 int i; 309 for (i = 0; i < ARRAY_SIZE(usnic_class_attributes); ++i) { 310 device_remove_file(&us_ibdev->ib_dev.dev, 311 usnic_class_attributes[i]); 312 } 313 314 kobject_put(us_ibdev->qpn_kobj); 315} 316 317void usnic_ib_sysfs_qpn_add(struct usnic_ib_qp_grp *qp_grp) 318{ 319 struct usnic_ib_dev *us_ibdev; 320 int err; 321 322 us_ibdev = qp_grp->vf->pf; 323 324 err = kobject_init_and_add(&qp_grp->kobj, &usnic_ib_qpn_type, 325 kobject_get(us_ibdev->qpn_kobj), 326 "%d", qp_grp->grp_id); 327 if (err) { 328 kobject_put(us_ibdev->qpn_kobj); 329 return; 330 } 331} 332 333void usnic_ib_sysfs_qpn_remove(struct usnic_ib_qp_grp *qp_grp) 334{ 335 struct usnic_ib_dev *us_ibdev; 336 337 us_ibdev = qp_grp->vf->pf; 338 339 kobject_put(&qp_grp->kobj); 340 kobject_put(us_ibdev->qpn_kobj); 341} 342