From: Yuval Shaia Date: Mon, 4 Apr 2016 13:29:12 +0000 (+0300) Subject: IB/ipoib: sysfs interface to manage ACL tables X-Git-Tag: v4.1.12-92~108^2~14 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=d1267b85fc2471c44d9971b990cd7bf88bcd2853;p=users%2Fjedix%2Flinux-maple.git IB/ipoib: sysfs interface to manage ACL tables Expose sysfs interface for ACL to be used for debug. Orabug: 23222944 Signed-off-by: Yuval Shaia Reviewed-by: Santosh Shilimkar --- diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index c1b49451424b1..bfdf3e4437b4f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -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; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_acl.c b/drivers/infiniband/ulp/ipoib/ipoib_acl.c index 8ff04a634e894..087f0d2e137fc 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_acl.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_acl.c @@ -28,8 +28,461 @@ * SOFTWARE. */ +#include +#include #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); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 60424e9623d88..1888f2c078303 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -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) { diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 02e3cc88aea49..24f1584188992 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -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; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index 9467032976cb3..b947ef9a8c4f8 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -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;