]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
IB/ipoib: sysfs interface to manage ACL tables
authorYuval Shaia <yuval.shaia@oracle.com>
Mon, 4 Apr 2016 13:29:12 +0000 (16:29 +0300)
committerChuck Anderson <chuck.anderson@oracle.com>
Tue, 12 Jul 2016 19:46:44 +0000 (12:46 -0700)
Expose sysfs interface for ACL to be used for debug.

Orabug: 23222944

Signed-off-by: Yuval Shaia <yuval.shaia@oracle.com>
Reviewed-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_acl.c
drivers/infiniband/ulp/ipoib/ipoib_cm.c
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_vlan.c

index c1b49451424b1357f190199f6d41ef46712359f4..bfdf3e4437b4fe04efa437838109f7bcaf111181 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/kref.h>
 #include <linux/if_infiniband.h>
 #include <linux/mutex.h>
+#include <linux/radix-tree.h>
 
 #include <net/neighbour.h>
 #include <net/sch_generic.h>
@@ -116,6 +117,8 @@ enum {
        IPOIB_NON_CHILD           = 0,
        IPOIB_LEGACY_CHILD        = 1,
        IPOIB_RTNL_CHILD          = 2,
+
+       ACL_BATCH_SZ              = 100,
 };
 
 #define        IPOIB_OP_RECV   (1ul << 31)
@@ -323,6 +326,19 @@ struct ipoib_qp_state_validate {
        struct ipoib_dev_priv   *priv;
 };
 
+#define DRIVER_ACL_NAME "_main_"
+#define INSTANCE_ACL_ID_SZ 80
+struct ipoib_instance_acl {
+       char                    name[INSTANCE_ACL_ID_SZ];
+       struct ib_cm_acl        acl;
+};
+
+struct ipoib_instances_acls {
+       struct radix_tree_root  instances; /* list of ipoib_instance_acl */
+       size_t                  list_count;
+       struct mutex            lock;
+};
+
 /*
  * Device private locking: network stack tx_lock protects members used
  * in TX fast path, lock protects everything else.  lock nests inside
@@ -416,6 +432,8 @@ struct ipoib_dev_priv {
        /* Device specific; obtained from query_device */
        unsigned max_sge;
        struct ib_cm_acl acl;
+       /* Used to diaplay instance ACLs, no actual use in driver */
+       struct ipoib_instances_acls instances_acls;
        int arp_blocked;
        int arp_accepted;
        int ud_blocked;
@@ -466,6 +484,9 @@ struct ipoib_neigh {
 #define IPOIB_UD_MTU(ib_mtu)           (ib_mtu - IPOIB_ENCAP_LEN)
 #define IPOIB_UD_BUF_SIZE(ib_mtu)      (ib_mtu + IB_GRH_BYTES)
 
+void print_acl_instances_to_buf(char *buf, size_t sz,
+                               struct ipoib_dev_priv *priv);
+
 void ipoib_neigh_dtor(struct ipoib_neigh *neigh);
 static inline void ipoib_neigh_put(struct ipoib_neigh *neigh)
 {
@@ -601,8 +622,14 @@ int ipoib_set_dev_features(struct ipoib_dev_priv *priv, struct ib_device *hca);
 /* We don't support UC connections at the moment */
 #define IPOIB_CM_SUPPORTED(ha)   (ha[0] & (IPOIB_FLAGS_RC))
 
+int ipoib_create_acl_sysfs(struct net_device *dev);
 void ipoib_init_acl(struct net_device *dev);
 void ipoib_clean_acl(struct net_device *dev);
+int ipoib_create_instance_acl(const char *name, struct net_device *dev);
+int ipoib_delete_instance_acl(const char *name, struct net_device *dev);
+struct ib_cm_acl *ipoib_get_instance_acl(const char *name,
+                                        struct net_device *dev);
+
 #ifdef CONFIG_INFINIBAND_IPOIB_CM
 
 extern int ipoib_max_conn_qp;
index 8ff04a634e8943efa90270702bb2ccd9a3857aaa..087f0d2e137fc7ed15779f41325008c8624b25df 100644 (file)
  * SOFTWARE.
  */
 
+#include <linux/module.h>
+#include <linux/jhash.h>
 #include "ipoib.h"
 
+int extract_guid_and_subnet(const char *buf, char *name, u64 *subnet_prefix,
+                           u64 *guid)
+{
+       u64 gid[8];
+       int i, shift;
+
+       memset(&gid, 0, sizeof(gid));
+
+       if (name) {
+               if (sscanf(buf,
+                          "%s %4llx:%4llx:%4llx:%4llx:%4llx:%4llx:%4llx:%4llx",
+                          name, &gid[0], &gid[1], &gid[2], &gid[3], &gid[4],
+                          &gid[5], &gid[6], &gid[7]) != 9)
+                       return -EINVAL;
+       } else
+               if (sscanf(buf,
+                          "%4llx:%4llx:%4llx:%4llx:%4llx:%4llx:%4llx:%4llx",
+                          &gid[0], &gid[1], &gid[2], &gid[3], &gid[4], &gid[5],
+                          &gid[6], &gid[7]) != 8)
+                       return -EINVAL;
+
+       *guid = 0;
+       *subnet_prefix = 0;
+       for (i = 0; i < 4; i++) {
+               shift = ((3 - i) * 16);
+               *subnet_prefix |= gid[i] << shift;
+               *guid |= gid[i + 4] << shift;
+       }
+
+       return 0;
+}
+
+int extract_guid_subnet_and_ip(const char *buf, char *name, u64 *subnet_prefix,
+                              u64 *guid, u32 *src_ip, char *uuid)
+{
+       u64 gid[8];
+       u32 ip[4];
+       int rc, i, shift;
+
+       memset(&gid, 0, sizeof(gid));
+       memset(&ip, 0, sizeof(ip));
+       memset(uuid, 0, UUID_SZ);
+
+       rc = sscanf(buf,
+               "%s %4llx:%4llx:%4llx:%4llx:%4llx:%4llx:%4llx:%4llx %s %d.%d.%d.%d",
+               name, &gid[0], &gid[1], &gid[2], &gid[3], &gid[4], &gid[5],
+               &gid[6], &gid[7], uuid, &ip[0], &ip[1],  &ip[2],  &ip[3]);
+       if (rc != 14)
+               return -EINVAL;
+
+       *guid = 0;
+       *subnet_prefix = 0;
+       for (i = 0; i < 4; i++) {
+               shift = ((3 - i) * 16);
+               *subnet_prefix |= gid[i] << shift;
+               *guid |= gid[i + 4] << shift;
+       }
+
+       *src_ip = 0;
+       for (i = 0; i < 4; i++) {
+               shift = ((3 - i) * 8);
+               *src_ip |= ip[i] << shift;
+       }
+
+       return 0;
+}
+
+static ssize_t show_acl_enabled(struct device *d,
+                               struct device_attribute *attr, char *buf)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(to_net_dev(d));
+
+       if (priv->acl.enabled)
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t set_acl_enabled(struct device *d, struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(to_net_dev(d));
+
+       priv->acl.enabled = strcmp(buf, "0\n");
+       return count;
+}
+
+static DEVICE_ATTR(acl_enabled, S_IWUSR | S_IRUGO, show_acl_enabled,
+                  set_acl_enabled);
+
+static ssize_t add_acl(struct device *d, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(to_net_dev(d));
+       int rc;
+       u64 guid, subnet_prefix;
+       u32 ip;
+       char uuid[UUID_SZ];
+       struct ib_cm_acl *instance_acl;
+       char name[INSTANCE_ACL_ID_SZ];
+
+       rc = extract_guid_subnet_and_ip(buf, name, &subnet_prefix, &guid, &ip,
+                                       uuid);
+       if (rc != 0)
+               return rc;
+
+       instance_acl = ipoib_get_instance_acl(name, to_net_dev(d));
+       if (!instance_acl)
+               return -EINVAL;
+
+       rc = ib_cm_acl_insert(instance_acl, subnet_prefix, guid, ip, uuid);
+       rc |= ib_cm_acl_insert(&priv->acl, subnet_prefix, guid, ip, uuid);
+       if (rc != 0)
+               return rc;
+
+       return count;
+}
+
+static DEVICE_ATTR(add_acl, S_IWUSR, NULL, add_acl);
+
+static ssize_t delete_acl(struct device *d, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(to_net_dev(d));
+       u64 guid, subnet_prefix;
+       int rc;
+       struct ib_cm_acl *instance_acl;
+       char name[INSTANCE_ACL_ID_SZ];
+
+       rc = extract_guid_and_subnet(buf, name, &subnet_prefix, &guid);
+       if (rc != 0)
+               return rc;
+
+       instance_acl = ipoib_get_instance_acl(name, to_net_dev(d));
+       if (!instance_acl)
+               return -EINVAL;
+
+       ib_cm_acl_delete(instance_acl, subnet_prefix, guid);
+       ib_cm_acl_delete(&priv->acl, subnet_prefix, guid);
+
+       return count;
+}
+
+static DEVICE_ATTR(delete_acl, S_IWUSR, NULL, delete_acl);
+
+void print_acl_to_buf(char *buf, const char *name, struct ib_cm_acl *acl)
+{
+       struct ib_cm_acl_elem *list;
+       ssize_t list_count, i;
+       u8 *subnet_prefix, *guid;
+       u8 *ip;
+
+       ib_cm_acl_scan(acl, &list, &list_count);
+       for (i = 0; i < list_count; i++) {
+               subnet_prefix = (u8 *)&(list[i].subnet_prefix);
+               guid = (u8 *)&(list[i].guid);
+               ip = (u8 *)&(list[i].ip);
+               sprintf(buf,
+                       "%s%s\t%d\t%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\t%s\t%d.%d.%d.%d\n",
+                       buf, name, list[i].ref_count, subnet_prefix[7],
+                       subnet_prefix[6], subnet_prefix[5], subnet_prefix[4],
+                       subnet_prefix[3], subnet_prefix[2], subnet_prefix[1],
+                       subnet_prefix[0], guid[7], guid[6], guid[5], guid[4],
+                       guid[3], guid[2], guid[1], guid[0], list[i].uuid,
+                       ip[3], ip[2], ip[1], ip[0]);
+       }
+       kfree(list);
+}
+
+static ssize_t show_acl(struct device *d,
+                       struct device_attribute *attr, char *buf)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(to_net_dev(d));
+       struct ipoib_instance_acl *results[ACL_BATCH_SZ];
+       unsigned int count, i;
+       unsigned long idx = 0;
+
+       strcpy(buf, "");
+
+       print_acl_to_buf(buf, DRIVER_ACL_NAME, &priv->acl);
+
+       count = 0;
+       do {
+               count = radix_tree_gang_lookup(&priv->instances_acls.instances,
+                                             (void **)results, idx,
+                                             ACL_BATCH_SZ);
+               for (i = 0; i < count; i++)
+                       print_acl_to_buf(buf, results[i]->name,
+                                        &results[i]->acl);
+               if (count)
+                       idx = jhash(results[i - 1]->name,
+                                   strlen(results[i - 1]->name), 0) + 1;
+       } while (count);
+
+       return strlen(buf);
+}
+
+static DEVICE_ATTR(acl, S_IRUGO, show_acl, NULL);
+
+void print_acl_instances_to_buf(char *buf, size_t sz,
+                               struct ipoib_dev_priv *priv)
+{
+       struct ipoib_instance_acl *results[ACL_BATCH_SZ];
+       unsigned int count, i;
+       unsigned long idx = 0;
+
+       if (sz == 0)
+               return;
+
+       strcpy(buf, "");
+
+       count = 0;
+       do {
+               count = radix_tree_gang_lookup(&priv->instances_acls.instances,
+                                             (void **)results, idx,
+                                             ACL_BATCH_SZ);
+               for (i = 0; i < count; i++) {
+                       if (sz &&
+                           (strlen(buf) + strlen(results[i]->name) + 1) > sz)
+                               return;
+                       sprintf(buf, "%s%s\n", buf, results[i]->name);
+               }
+
+               if (count)
+                       idx = jhash(results[i - 1]->name,
+                                   strlen(results[i - 1]->name), 0) + 1;
+       } while (count);
+}
+
+static ssize_t show_acl_instances(struct device *d,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(to_net_dev(d));
+
+       /* Assumption here is that buf has enoght place to hold entire list */
+       print_acl_instances_to_buf(buf, priv->instances_acls.list_count *
+                                  INSTANCE_ACL_ID_SZ + 1, priv);
+
+       return strlen(buf);
+}
+
+static DEVICE_ATTR(acl_instances, S_IRUGO, show_acl_instances, NULL);
+
+static ssize_t add_acl_instance(struct device *d, struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       char name[INSTANCE_ACL_ID_SZ];
+       char *crlf_pos = strchr(buf, '\n');
+
+       strncpy(name, buf, INSTANCE_ACL_ID_SZ);
+       if (crlf_pos)
+               name[crlf_pos - buf] = 0;
+       ipoib_create_instance_acl(name, to_net_dev(d));
+
+       return count;
+}
+
+static DEVICE_ATTR(add_acl_instance, S_IWUSR, NULL, add_acl_instance);
+
+static ssize_t delete_acl_instance(struct device *d,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       char name[INSTANCE_ACL_ID_SZ];
+       char *crlf_pos = strchr(buf, '\n');
+
+       strncpy(name, buf, INSTANCE_ACL_ID_SZ);
+       if (crlf_pos)
+               name[crlf_pos - buf] = 0;
+       ipoib_delete_instance_acl(name, to_net_dev(d));
+
+       return count;
+}
+
+static DEVICE_ATTR(delete_acl_instance, S_IWUSR, NULL, delete_acl_instance);
+
+int ipoib_create_acl_sysfs(struct net_device *dev)
+{
+       int rc = 0;
+
+       rc = device_create_file(&dev->dev, &dev_attr_acl_enabled);
+       if (rc)
+               return rc;
+       rc = device_create_file(&dev->dev, &dev_attr_add_acl);
+       if (rc)
+               return rc;
+       rc = device_create_file(&dev->dev, &dev_attr_delete_acl);
+       if (rc)
+               return rc;
+       rc = device_create_file(&dev->dev, &dev_attr_acl);
+       if (rc)
+               return rc;
+       rc = device_create_file(&dev->dev, &dev_attr_add_acl_instance);
+       if (rc)
+               return rc;
+       rc = device_create_file(&dev->dev, &dev_attr_delete_acl_instance);
+       if (rc)
+               return rc;
+       rc = device_create_file(&dev->dev, &dev_attr_acl_instances);
+       if (rc)
+               return rc;
+
+       return 0;
+}
+
+void delete_instance_acls(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_instance_acl *results[ACL_BATCH_SZ];
+       unsigned int count, i;
+       unsigned long idx = 0;
+
+       count = 0;
+       do {
+               count = radix_tree_gang_lookup(&priv->instances_acls.instances,
+                                             (void **)results, idx,
+                                             ACL_BATCH_SZ);
+               for (i = 0; i < count; i++) {
+                       ipoib_dbg(priv, "Clean instance ACL %s\n",
+                                 results[i]->name);
+                       ipoib_delete_instance_acl(results[i]->name, dev);
+               }
+               if (count)
+                       idx = jhash(results[i - 1]->name,
+                                   strlen(results[i - 1]->name), 0) + 1;
+       } while (count);
+}
+
+int ipoib_create_instance_acl(const char *name, struct net_device *dev)
+{
+       u32 inst_name_hash;
+       struct ipoib_instance_acl *instance_acl;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int rc = 0;
+
+       if (strlen(name) > INSTANCE_ACL_ID_SZ)
+               return -EINVAL;
+
+       inst_name_hash = jhash(name, strlen(name), 0);
+       mutex_lock(&priv->instances_acls.lock);
+       if (radix_tree_lookup(&priv->instances_acls.instances, inst_name_hash))
+               goto err_exist;
+
+       instance_acl = (struct ipoib_instance_acl *)
+                      kmalloc(sizeof(struct ipoib_instance_acl), GFP_KERNEL);
+       if (!instance_acl)
+               goto err_nomem;
+
+       strcpy(instance_acl->name, name);
+       ib_cm_acl_init(&instance_acl->acl);
+
+       rc = radix_tree_insert(&priv->instances_acls.instances, inst_name_hash,
+                              instance_acl);
+       if (rc != 0)
+               goto err_radix;
+
+       priv->instances_acls.list_count++;
+
+       __module_get(THIS_MODULE);
+
+       goto out;
+
+err_exist:
+       ipoib_err(priv, "Instance ACL %s already exist\n", name);
+       rc = -EEXIST;
+       goto out;
+
+err_nomem:
+       ipoib_err(priv, "No memory to create Instance ACL %s\n", name);
+       rc = -ENOMEM;
+       goto out;
+
+err_radix:
+       ipoib_err(priv, "Error %d while trying to add %s\n", rc, name);
+       kfree(instance_acl);
+
+out:
+       mutex_unlock(&priv->instances_acls.lock);
+       return rc;
+}
+
+int ipoib_delete_instance_acl(const char *name, struct net_device *dev)
+{
+       u32 inst_name_hash;
+       struct ipoib_instance_acl *instance_acl;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ib_cm_acl_elem *list;
+       ssize_t list_count, i;
+       int rc = 0;
+
+       inst_name_hash = jhash(name, strlen(name), 0);
+       mutex_lock(&priv->instances_acls.lock);
+       instance_acl = (struct ipoib_instance_acl *)
+                      radix_tree_delete(&priv->instances_acls.instances,
+                                        inst_name_hash);
+       if (!instance_acl)
+               goto err_notexist;
+
+       /* Decrease reference count in main ACL */
+       ib_cm_acl_scan(&instance_acl->acl, &list, &list_count);
+       for (i = 0; i < list_count; i++) {
+               /* Clean all referrence */
+               do
+                       ib_cm_acl_delete(&priv->acl, list[i].subnet_prefix,
+                                        list[i].guid);
+               while (ib_cm_acl_delete(&(instance_acl->acl),
+                      list[i].subnet_prefix, list[i].guid));
+       }
+       kfree(list);
+
+       ib_cm_acl_clean(&(instance_acl->acl));
+
+       kfree(instance_acl);
+
+       priv->instances_acls.list_count--;
+
+       module_put(THIS_MODULE);
+
+       goto out;
+
+err_notexist:
+       ipoib_err(priv, "Instance ACL %s is not exist\n", name);
+       rc = -EINVAL;
+       goto out;
+
+out:
+       mutex_unlock(&priv->instances_acls.lock);
+       return rc;
+}
+
+struct ib_cm_acl *ipoib_get_instance_acl(const char *name,
+                                        struct net_device *dev)
+{
+       u32 inst_name_hash;
+       struct ipoib_instance_acl *instance_acl;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       inst_name_hash = jhash(name, strlen(name), 0);
+       mutex_lock(&priv->instances_acls.lock);
+       instance_acl = (struct ipoib_instance_acl *)
+                      radix_tree_lookup(&priv->instances_acls.instances,
+                                        inst_name_hash);
+       mutex_unlock(&priv->instances_acls.lock);
+       if (!instance_acl) {
+               ipoib_err(priv, "Instance ACL %s is not exist\n", name);
+               return 0;
+       }
+
+       return &(instance_acl->acl);
+}
+
 void ipoib_init_acl(struct net_device *dev)
 {
        struct ib_cm_dpp dpp;
@@ -45,6 +498,8 @@ void ipoib_clean_acl(struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
 
+       delete_instance_acls(dev);
+
        ipoib_dbg(priv, "Clean ACL for device %s\n", dev->name);
        ib_cm_unregister_acl(&priv->acl);
        ib_cm_acl_clean(&priv->acl);
index 60424e9623d888a008bcf524f158921aa405e283..1888f2c07830382d6e63910762136fcec1d1fe96 100644 (file)
@@ -966,6 +966,10 @@ int ipoib_cm_dev_open(struct net_device *dev)
                goto err_cm;
        }
 
+       INIT_RADIX_TREE(&priv->instances_acls.instances, GFP_KERNEL);
+       priv->instances_acls.list_count = 0;
+       mutex_init(&priv->instances_acls.lock);
+
        ret = ib_cm_listen(priv->cm.id, cpu_to_be64(IPOIB_CM_IETF_ID | priv->qp->qp_num),
                           0, NULL);
        if (ret) {
index 02e3cc88aea49af9aa2a4e09a343fccf8bbe0efa..24f15841889928087947fe5063c7f5519707a82d 100644 (file)
@@ -1887,7 +1887,9 @@ static struct net_device *ipoib_add_port(const char *format,
                goto sysfs_failed;
        if (device_create_file(&priv->dev->dev, &dev_attr_delete_named_child))
                goto sysfs_failed;
-
+       if (ipoib_debug_level)
+               if (ipoib_create_acl_sysfs(priv->dev))
+                       goto sysfs_failed;
 
        return priv->dev;
 
index 9467032976cb340428c118ef5c80059ce025756c..b947ef9a8c4f8bbb40bac39b5b27d67bc6240cbf 100644 (file)
@@ -100,6 +100,10 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
 
                if (device_create_file(&priv->dev->dev, &dev_attr_parent))
                        goto sysfs_failed;
+
+               if (ipoib_debug_level)
+                       if (ipoib_create_acl_sysfs(priv->dev))
+                               goto sysfs_failed;
        }
 
        priv->child_type  = type;