Merge tag 'rdma-for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[firefly-linux-kernel-4.4.55.git] / drivers / infiniband / hw / qib / qib_sysfs.c
index dd9cd49d097927bf8816c5aedc836c3f31e4b5ea..034cc821de5ce9e5172046ee7b05f1fc822c5e88 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2006, 2007, 2008, 2009 QLogic Corporation. All rights reserved.
+ * Copyright (c) 2012 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
  * Copyright (c) 2006 PathScale, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
 #include <linux/ctype.h>
 
 #include "qib.h"
-
-/**
- * qib_parse_ushort - parse an unsigned short value in an arbitrary base
- * @str: the string containing the number
- * @valp: where to put the result
- *
- * Returns the number of bytes consumed, or negative value on error.
- */
-static int qib_parse_ushort(const char *str, unsigned short *valp)
-{
-       unsigned long val;
-       char *end;
-       int ret;
-
-       if (!isdigit(str[0])) {
-               ret = -EINVAL;
-               goto bail;
-       }
-
-       val = simple_strtoul(str, &end, 0);
-
-       if (val > 0xffff) {
-               ret = -EINVAL;
-               goto bail;
-       }
-
-       *valp = val;
-
-       ret = end + 1 - str;
-       if (ret == 0)
-               ret = -EINVAL;
-
-bail:
-       return ret;
-}
+#include "qib_mad.h"
 
 /* start of per-port functions */
 /*
@@ -90,7 +57,11 @@ static ssize_t store_hrtbt_enb(struct qib_pportdata *ppd, const char *buf,
        int ret;
        u16 val;
 
-       ret = qib_parse_ushort(buf, &val);
+       ret = kstrtou16(buf, 0, &val);
+       if (ret) {
+               qib_dev_err(dd, "attempt to set invalid Heartbeat enable\n");
+               return ret;
+       }
 
        /*
         * Set the "intentional" heartbeat enable per either of
@@ -99,10 +70,7 @@ static ssize_t store_hrtbt_enb(struct qib_pportdata *ppd, const char *buf,
         * because entering loopback mode overrides it and automatically
         * disables heartbeat.
         */
-       if (ret >= 0)
-               ret = dd->f_set_ib_cfg(ppd, QIB_IB_CFG_HRTBT, val);
-       if (ret < 0)
-               qib_dev_err(dd, "attempt to set invalid Heartbeat enable\n");
+       ret = dd->f_set_ib_cfg(ppd, QIB_IB_CFG_HRTBT, val);
        return ret < 0 ? ret : count;
 }
 
@@ -126,12 +94,14 @@ static ssize_t store_led_override(struct qib_pportdata *ppd, const char *buf,
        int ret;
        u16 val;
 
-       ret = qib_parse_ushort(buf, &val);
-       if (ret > 0)
-               qib_set_led_override(ppd, val);
-       else
+       ret = kstrtou16(buf, 0, &val);
+       if (ret) {
                qib_dev_err(dd, "attempt to set invalid LED override\n");
-       return ret < 0 ? ret : count;
+               return ret;
+       }
+
+       qib_set_led_override(ppd, val);
+       return count;
 }
 
 static ssize_t show_status(struct qib_pportdata *ppd, char *buf)
@@ -231,6 +201,98 @@ static struct attribute *port_default_attributes[] = {
        NULL
 };
 
+/*
+ * Start of per-port congestion control structures and support code
+ */
+
+/*
+ * Congestion control table size followed by table entries
+ */
+static ssize_t read_cc_table_bin(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *bin_attr,
+               char *buf, loff_t pos, size_t count)
+{
+       int ret;
+       struct qib_pportdata *ppd =
+               container_of(kobj, struct qib_pportdata, pport_cc_kobj);
+
+       if (!qib_cc_table_size || !ppd->ccti_entries_shadow)
+               return -EINVAL;
+
+       ret = ppd->total_cct_entry * sizeof(struct ib_cc_table_entry_shadow)
+                + sizeof(__be16);
+
+       if (pos > ret)
+               return -EINVAL;
+
+       if (count > ret - pos)
+               count = ret - pos;
+
+       if (!count)
+               return count;
+
+       spin_lock(&ppd->cc_shadow_lock);
+       memcpy(buf, ppd->ccti_entries_shadow, count);
+       spin_unlock(&ppd->cc_shadow_lock);
+
+       return count;
+}
+
+static void qib_port_release(struct kobject *kobj)
+{
+       /* nothing to do since memory is freed by qib_free_devdata() */
+}
+
+static struct kobj_type qib_port_cc_ktype = {
+       .release = qib_port_release,
+};
+
+static struct bin_attribute cc_table_bin_attr = {
+       .attr = {.name = "cc_table_bin", .mode = 0444},
+       .read = read_cc_table_bin,
+       .size = PAGE_SIZE,
+};
+
+/*
+ * Congestion settings: port control, control map and an array of 16
+ * entries for the congestion entries - increase, timer, event log
+ * trigger threshold and the minimum injection rate delay.
+ */
+static ssize_t read_cc_setting_bin(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *bin_attr,
+               char *buf, loff_t pos, size_t count)
+{
+       int ret;
+       struct qib_pportdata *ppd =
+               container_of(kobj, struct qib_pportdata, pport_cc_kobj);
+
+       if (!qib_cc_table_size || !ppd->congestion_entries_shadow)
+               return -EINVAL;
+
+       ret = sizeof(struct ib_cc_congestion_setting_attr_shadow);
+
+       if (pos > ret)
+               return -EINVAL;
+       if (count > ret - pos)
+               count = ret - pos;
+
+       if (!count)
+               return count;
+
+       spin_lock(&ppd->cc_shadow_lock);
+       memcpy(buf, ppd->congestion_entries_shadow, count);
+       spin_unlock(&ppd->cc_shadow_lock);
+
+       return count;
+}
+
+static struct bin_attribute cc_setting_bin_attr = {
+       .attr = {.name = "cc_settings_bin", .mode = 0444},
+       .read = read_cc_setting_bin,
+       .size = PAGE_SIZE,
+};
+
+
 static ssize_t qib_portattr_show(struct kobject *kobj,
        struct attribute *attr, char *buf)
 {
@@ -253,10 +315,6 @@ static ssize_t qib_portattr_store(struct kobject *kobj,
        return pattr->store(ppd, buf, len);
 }
 
-static void qib_port_release(struct kobject *kobj)
-{
-       /* nothing to do since memory is freed by qib_free_devdata() */
-}
 
 static const struct sysfs_ops qib_port_ops = {
        .show = qib_portattr_show,
@@ -411,12 +469,12 @@ static ssize_t diagc_attr_store(struct kobject *kobj, struct attribute *attr,
        struct qib_pportdata *ppd =
                container_of(kobj, struct qib_pportdata, diagc_kobj);
        struct qib_ibport *qibp = &ppd->ibport_data;
-       char *endp;
-       long val = simple_strtol(buf, &endp, 0);
-
-       if (val < 0 || endp == buf)
-               return -EINVAL;
+       u32 val;
+       int ret;
 
+       ret = kstrtou32(buf, 0, &val);
+       if (ret)
+               return ret;
        *(u32 *)((char *) qibp + dattr->counter) = val;
        return size;
 }
@@ -649,8 +707,9 @@ int qib_create_port_files(struct ib_device *ibdev, u8 port_num,
        int ret;
 
        if (!port_num || port_num > dd->num_pports) {
-               qib_dev_err(dd, "Skipping infiniband class with "
-                           "invalid port %u\n", port_num);
+               qib_dev_err(dd,
+                       "Skipping infiniband class with invalid port %u\n",
+                       port_num);
                ret = -ENODEV;
                goto bail;
        }
@@ -659,8 +718,9 @@ int qib_create_port_files(struct ib_device *ibdev, u8 port_num,
        ret = kobject_init_and_add(&ppd->pport_kobj, &qib_port_ktype, kobj,
                                   "linkcontrol");
        if (ret) {
-               qib_dev_err(dd, "Skipping linkcontrol sysfs info, "
-                           "(err %d) port %u\n", ret, port_num);
+               qib_dev_err(dd,
+                       "Skipping linkcontrol sysfs info, (err %d) port %u\n",
+                       ret, port_num);
                goto bail;
        }
        kobject_uevent(&ppd->pport_kobj, KOBJ_ADD);
@@ -668,26 +728,70 @@ int qib_create_port_files(struct ib_device *ibdev, u8 port_num,
        ret = kobject_init_and_add(&ppd->sl2vl_kobj, &qib_sl2vl_ktype, kobj,
                                   "sl2vl");
        if (ret) {
-               qib_dev_err(dd, "Skipping sl2vl sysfs info, "
-                           "(err %d) port %u\n", ret, port_num);
-               goto bail_sl;
+               qib_dev_err(dd,
+                       "Skipping sl2vl sysfs info, (err %d) port %u\n",
+                       ret, port_num);
+               goto bail_link;
        }
        kobject_uevent(&ppd->sl2vl_kobj, KOBJ_ADD);
 
        ret = kobject_init_and_add(&ppd->diagc_kobj, &qib_diagc_ktype, kobj,
                                   "diag_counters");
        if (ret) {
-               qib_dev_err(dd, "Skipping diag_counters sysfs info, "
-                           "(err %d) port %u\n", ret, port_num);
-               goto bail_diagc;
+               qib_dev_err(dd,
+                       "Skipping diag_counters sysfs info, (err %d) port %u\n",
+                       ret, port_num);
+               goto bail_sl;
        }
        kobject_uevent(&ppd->diagc_kobj, KOBJ_ADD);
 
+       if (!qib_cc_table_size || !ppd->congestion_entries_shadow)
+               return 0;
+
+       ret = kobject_init_and_add(&ppd->pport_cc_kobj, &qib_port_cc_ktype,
+                               kobj, "CCMgtA");
+       if (ret) {
+               qib_dev_err(dd,
+                "Skipping Congestion Control sysfs info, (err %d) port %u\n",
+                ret, port_num);
+               goto bail_diagc;
+       }
+
+       kobject_uevent(&ppd->pport_cc_kobj, KOBJ_ADD);
+
+       ret = sysfs_create_bin_file(&ppd->pport_cc_kobj,
+                               &cc_setting_bin_attr);
+       if (ret) {
+               qib_dev_err(dd,
+                "Skipping Congestion Control setting sysfs info, (err %d) port %u\n",
+                ret, port_num);
+               goto bail_cc;
+       }
+
+       ret = sysfs_create_bin_file(&ppd->pport_cc_kobj,
+                               &cc_table_bin_attr);
+       if (ret) {
+               qib_dev_err(dd,
+                "Skipping Congestion Control table sysfs info, (err %d) port %u\n",
+                ret, port_num);
+               goto bail_cc_entry_bin;
+       }
+
+       qib_devinfo(dd->pcidev,
+               "IB%u: Congestion Control Agent enabled for port %d\n",
+               dd->unit, port_num);
+
        return 0;
 
+bail_cc_entry_bin:
+       sysfs_remove_bin_file(&ppd->pport_cc_kobj, &cc_setting_bin_attr);
+bail_cc:
+       kobject_put(&ppd->pport_cc_kobj);
 bail_diagc:
-       kobject_put(&ppd->sl2vl_kobj);
+       kobject_put(&ppd->diagc_kobj);
 bail_sl:
+       kobject_put(&ppd->sl2vl_kobj);
+bail_link:
        kobject_put(&ppd->pport_kobj);
 bail:
        return ret;
@@ -720,7 +824,15 @@ void qib_verbs_unregister_sysfs(struct qib_devdata *dd)
 
        for (i = 0; i < dd->num_pports; i++) {
                ppd = &dd->pport[i];
-               kobject_put(&ppd->pport_kobj);
+               if (qib_cc_table_size &&
+                       ppd->congestion_entries_shadow) {
+                       sysfs_remove_bin_file(&ppd->pport_cc_kobj,
+                               &cc_setting_bin_attr);
+                       sysfs_remove_bin_file(&ppd->pport_cc_kobj,
+                               &cc_table_bin_attr);
+                       kobject_put(&ppd->pport_cc_kobj);
+               }
                kobject_put(&ppd->sl2vl_kobj);
+               kobject_put(&ppd->pport_kobj);
        }
 }