From: Rolf Manderscheid Date: Tue, 11 Dec 2007 17:12:03 +0000 (-0800) Subject: ib/ipoib: support multiple devices on the same partition X-Git-Url: https://openfabrics.org/gitweb/?a=commitdiff_plain;h=eae4bccba7342a0f64597e79edd1ccfa3c0a9ed6;p=~shefty%2Frdma-dev.git ib/ipoib: support multiple devices on the same partition Multiple devices can coexist in a partition provided they have different scopes. This patch adds an optional scope argument to the create_child / delete_child attributes. Created child devices are still named the same way as before, so in order to create multiple child devices in the same partition, an interface name change will be necessary to make room for subsequent children, eg: echo 0xffff 0xe > /sys/class/net/ib0/create_child ip link set ib0.ffff name ib0.global Signed-off-by: Rolf Manderscheid --- diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index eb7edab0e83..2802ad2af42 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -446,13 +446,29 @@ void ipoib_transport_dev_cleanup(struct net_device *dev); void ipoib_event(struct ib_event_handler *handler, struct ib_event *record); -int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey); -int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey); +int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey, + unsigned char scope); +int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey, + unsigned char scope); void ipoib_pkey_poll(struct work_struct *work); int ipoib_pkey_dev_delay_open(struct net_device *dev); void ipoib_drain_cq(struct net_device *dev); +int ipoib_pkey_scope_in_use(struct net_device *dev, unsigned short pkey, + unsigned char scope); + +static inline unsigned char ipoib_get_scope(struct net_device *dev) +{ + return dev->broadcast[5] & 0xF; +} + +static inline void ipoib_set_scope(struct net_device *dev, unsigned char scope) +{ + dev->broadcast[5] &= ~0xF; + dev->broadcast[5] |= scope; +} + #ifdef CONFIG_INFINIBAND_IPOIB_CM #define IPOIB_FLAGS_RC 0x80 diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 83a8f177a1d..6ce0a8865ce 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -79,6 +79,8 @@ static const u8 ipv4_bcast_addr[] = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; +static const u8 default_scope = 2; /* link local */ + struct workqueue_struct *ipoib_workqueue; struct ib_sa_client ipoib_sa_client; @@ -1056,9 +1058,20 @@ int ipoib_add_umcast_attr(struct net_device *dev) static ssize_t show_bcast_scope(struct device *dev, struct device_attribute *attr, char *buf) { - struct ipoib_dev_priv *priv = netdev_priv(to_net_dev(dev)); + return sprintf(buf, "0x%x\n", (int) ipoib_get_scope(to_net_dev(dev))); +} - return sprintf(buf, "0x%x\n", priv->dev->broadcast[5] & 0xF); +static int valid_scope(unsigned char scope) +{ + switch (scope) { + case 0x2: /* link-local */ + case 0x5: /* site-local */ + case 0x8: /* organization-local */ + case 0xE: /* global */ + return 1; + default: + return 0; + } } static ssize_t set_bcast_scope(struct device *dev, @@ -1071,21 +1084,16 @@ static ssize_t set_bcast_scope(struct device *dev, if (priv->dev->flags & IFF_UP) return -EBUSY; - if (sscanf(buf, "%i", &scope) != 1) + if (sscanf(buf, "%i", &scope) != 1 || !valid_scope(scope)) return -EINVAL; - switch (scope) { - case 0x2: /* link-local */ - case 0x5: /* site-local */ - case 0x8: /* organization-local */ - case 0xE: /* global */ - break; - default: - return -EINVAL; - } + if (ipoib_get_scope(priv->dev) == scope) /* no change */ + return count; - priv->dev->broadcast[5] &= ~0xF; - priv->dev->broadcast[5] |= scope; + if (ipoib_pkey_scope_in_use(priv->dev, priv->pkey, scope)) + return -ENOTUNIQ; + + ipoib_set_scope(priv->dev, scope); return count; } static DEVICE_ATTR(broadcast_scope, S_IWUSR | S_IRUGO, show_bcast_scope, set_bcast_scope); @@ -1095,21 +1103,25 @@ static ssize_t create_child(struct device *dev, const char *buf, size_t count) { int pkey; + int scope = default_scope; int ret; - if (sscanf(buf, "%i", &pkey) != 1) + if (sscanf(buf, "%i %i", &pkey, &scope) < 1) return -EINVAL; if (pkey < 0 || pkey > 0xffff) return -EINVAL; + if (!valid_scope(scope)) + return -EINVAL; + /* * Set the full membership bit, so that we join the right * broadcast group, etc. */ pkey |= 0x8000; - ret = ipoib_vlan_add(to_net_dev(dev), pkey); + ret = ipoib_vlan_add(to_net_dev(dev), pkey, scope); return ret ? ret : count; } @@ -1120,21 +1132,44 @@ static ssize_t delete_child(struct device *dev, const char *buf, size_t count) { int pkey; + int scope = default_scope; int ret; - if (sscanf(buf, "%i", &pkey) != 1) + if (sscanf(buf, "%i %i", &pkey, &scope) < 1) return -EINVAL; if (pkey < 0 || pkey > 0xffff) return -EINVAL; - ret = ipoib_vlan_delete(to_net_dev(dev), pkey); + if (!valid_scope(scope)) + return -EINVAL; + + ret = ipoib_vlan_delete(to_net_dev(dev), pkey, scope); return ret ? ret : count; } static DEVICE_ATTR(delete_child, S_IWUGO, NULL, delete_child); +int ipoib_pkey_scope_in_use(struct net_device *dev, unsigned short pkey, unsigned char scope) +{ + struct ipoib_dev_priv *ppriv, *priv; + + ppriv = netdev_priv(dev); + /* + * We check the parent device and then all of the child interfaces to make sure + * the Pkey and scope don't match. + */ + if (ppriv->pkey == pkey && ipoib_get_scope(dev) == scope) + return 1; + + list_for_each_entry(priv, &ppriv->child_intfs, list) + if (priv->pkey == pkey && ipoib_get_scope(priv->dev) == scope) + return 1; + + return 0; +} + int ipoib_add_pkey_attr(struct net_device *dev) { return device_create_file(&dev->dev, &dev_attr_pkey); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index 293f5b892e3..280556f22ad 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -52,7 +52,8 @@ static ssize_t show_parent(struct device *d, struct device_attribute *attr, } static DEVICE_ATTR(parent, S_IRUGO, show_parent, NULL); -int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) +int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey, + unsigned char scope) { struct ipoib_dev_priv *ppriv, *priv; char intf_name[IFNAMSIZ]; @@ -65,22 +66,11 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) mutex_lock(&ppriv->vlan_mutex); - /* - * First ensure this isn't a duplicate. We check the parent device and - * then all of the child interfaces to make sure the Pkey doesn't match. - */ - if (ppriv->pkey == pkey) { + if (ipoib_pkey_scope_in_use(pdev, pkey, scope)) { result = -ENOTUNIQ; goto err; } - list_for_each_entry(priv, &ppriv->child_intfs, list) { - if (priv->pkey == pkey) { - result = -ENOTUNIQ; - goto err; - } - } - snprintf(intf_name, sizeof intf_name, "%s.%04x", ppriv->dev->name, pkey); priv = ipoib_intf_alloc(intf_name); @@ -97,6 +87,8 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) priv->dev->broadcast[8] = pkey >> 8; priv->dev->broadcast[9] = pkey & 0xff; + ipoib_set_scope(priv->dev, scope); + result = ipoib_dev_init(priv->dev, ppriv->ca, ppriv->port); if (result < 0) { ipoib_warn(ppriv, "failed to initialize subinterface: " @@ -146,7 +138,8 @@ err: return result; } -int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey) +int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey, + unsigned char scope) { struct ipoib_dev_priv *ppriv, *priv, *tpriv; int ret = -ENOENT; @@ -158,7 +151,7 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey) mutex_lock(&ppriv->vlan_mutex); list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) { - if (priv->pkey == pkey) { + if (priv->pkey == pkey && ipoib_get_scope(priv->dev) == scope) { unregister_netdev(priv->dev); ipoib_dev_cleanup(priv->dev); list_del(&priv->list);