Use flex array to increase maximum number of vports. Now maximum
number of vports are limited by flex-array limit (261K for 64-bit)
per bridge.

Signed-off-by: Pravin B Shelar <[email protected]>

Bug #2462
---
 datapath/actions.c     |    2 +-
 datapath/datapath.c    |   44 ++++++++++++++++++++++++++++++++------------
 datapath/datapath.h    |   24 ++++++++++++++++++++++--
 datapath/dp_sysfs_dp.c |    4 ++--
 datapath/dp_sysfs_if.c |    2 +-
 5 files changed, 58 insertions(+), 18 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index 824791d..cc238c4 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -248,7 +248,7 @@ static int do_output(struct datapath *dp, struct sk_buff 
*skb, int out_port)
        if (unlikely(!skb))
                return -ENOMEM;
 
-       vport = rcu_dereference(dp->ports[out_port]);
+       vport = ovs_vport(dp, out_port);
        if (unlikely(!vport)) {
                kfree_skb(skb);
                return -ENODEV;
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 220c7dd..dcb034a 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -120,7 +120,7 @@ static struct datapath *get_dp(struct net *net, int 
dp_ifindex)
 /* Must be called with rcu_read_lock or RTNL lock. */
 const char *ovs_dp_name(const struct datapath *dp)
 {
-       struct vport *vport = rcu_dereference_rtnl(dp->ports[OVSP_LOCAL]);
+       struct vport *vport = ovs_vport_check(dp, OVSP_LOCAL);
        return vport->ops->get_name(vport);
 }
 
@@ -131,7 +131,7 @@ static int get_dpifindex(struct datapath *dp)
 
        rcu_read_lock();
 
-       local = rcu_dereference(dp->ports[OVSP_LOCAL]);
+       local = ovs_vport(dp, OVSP_LOCAL);
        if (local)
                ifindex = local->ops->get_ifindex(local);
        else
@@ -244,9 +244,15 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
        ovs_flow_tbl_destroy((__force struct flow_table *)dp->table);
        free_percpu(dp->stats_percpu);
        release_net(ovs_dp_get_net(dp));
+       flex_array_free_parts(&dp->ports);
        kobject_put(&dp->ifobj);
 }
 
+static int dp_set_vport(struct datapath *dp, int port_no, struct vport *p)
+{
+       return flex_array_put(&dp->ports, port_no, &p, GFP_KERNEL|__GFP_ZERO);
+}
+
 /* Called with RTNL lock and genl_lock. */
 static struct vport *new_vport(const struct vport_parms *parms)
 {
@@ -255,13 +261,20 @@ static struct vport *new_vport(const struct vport_parms 
*parms)
        vport = ovs_vport_add(parms);
        if (!IS_ERR(vport)) {
                struct datapath *dp = parms->dp;
+               int err;
 
-               rcu_assign_pointer(dp->ports[parms->port_no], vport);
+               err = dp_set_vport(dp, parms->port_no, vport);
+               if (err) {
+                       vport = ERR_PTR(err);
+                       goto err;
+               }
                list_add(&vport->node, &dp->port_list);
 
                dp_ifinfo_notify(RTM_NEWLINK, vport);
        }
-
+       return vport;
+err:
+       ovs_vport_del(vport);
        return vport;
 }
 
@@ -276,7 +289,7 @@ void ovs_dp_detach_port(struct vport *p)
 
        /* First drop references to device. */
        list_del(&p->node);
-       rcu_assign_pointer(p->dp->ports[p->port_no], NULL);
+       dp_set_vport(p->dp, p->port_no, NULL);
 
        /* Then destroy it. */
        ovs_vport_del(p);
@@ -1391,6 +1404,11 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
        }
        ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
 
+       err = flex_array_init(&dp->ports, sizeof(struct vport *),
+                             DP_MAX_PORTS, GFP_KERNEL|__GFP_ZERO);
+       if (err)
+               goto err_destroy_percpu;
+
        /* Set up our datapath device. */
        parms.name = nla_data(a[OVS_DP_ATTR_NAME]);
        parms.type = OVS_VPORT_TYPE_INTERNAL;
@@ -1405,7 +1423,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
                if (err == -EBUSY)
                        err = -EEXIST;
 
-               goto err_destroy_percpu;
+               goto err_destroy_ports_array;
        }
 
        reply = ovs_dp_cmd_build_info(dp, info->snd_pid,
@@ -1426,7 +1444,9 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
        return 0;
 
 err_destroy_local_port:
-       ovs_dp_detach_port(rtnl_dereference(dp->ports[OVSP_LOCAL]));
+       ovs_dp_detach_port(ovs_vport_protected(dp, OVSP_LOCAL));
+err_destroy_ports_array:
+       flex_array_free_parts(&dp->ports);
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
 err_destroy_table:
@@ -1451,7 +1471,7 @@ static void __dp_destroy(struct datapath *dp)
 
        ovs_dp_sysfs_del_dp(dp);
        list_del(&dp->list_node);
-       ovs_dp_detach_port(rtnl_dereference(dp->ports[OVSP_LOCAL]));
+       ovs_dp_detach_port(ovs_vport_protected(dp, OVSP_LOCAL));
 
        /* rtnl_unlock() will wait until all the references to devices that
         * are pending unregistration have been dropped.  We do it here to
@@ -1705,7 +1725,7 @@ static struct vport *lookup_vport(struct net *net,
                if (!dp)
                        return ERR_PTR(-ENODEV);
 
-               vport = rcu_dereference_rtnl(dp->ports[port_no]);
+               vport = ovs_vport_check(dp, port_no);
                if (!vport)
                        return ERR_PTR(-ENOENT);
                return vport;
@@ -1761,7 +1781,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
                if (port_no >= DP_MAX_PORTS)
                        goto exit_unlock;
 
-               vport = rtnl_dereference(dp->ports[port_no]);
+               vport = ovs_vport_protected(dp, port_no);
                err = -EBUSY;
                if (vport)
                        goto exit_unlock;
@@ -1771,7 +1791,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct 
genl_info *info)
                                err = -EFBIG;
                                goto exit_unlock;
                        }
-                       vport = rtnl_dereference(dp->ports[port_no]);
+                       vport = ovs_vport_protected(dp, port_no);
                        if (!vport)
                                break;
                }
@@ -1947,7 +1967,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct 
netlink_callback *cb)
        for (port_no = cb->args[0]; port_no < DP_MAX_PORTS; port_no++) {
                struct vport *vport;
 
-               vport = rcu_dereference(dp->ports[port_no]);
+               vport = ovs_vport(dp, port_no);
                if (!vport)
                        continue;
 
diff --git a/datapath/datapath.h b/datapath/datapath.h
index b012a76..05f6de8 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -34,7 +34,10 @@
 #include "vlan.h"
 #include "vport.h"
 
-#define DP_MAX_PORTS 1024
+/* Flex array limit. */
+#define DP_MAX_PORTS (FLEX_ARRAY_ELEMENTS_PER_PART(sizeof(struct vport *)) *  \
+                     FLEX_ARRAY_NR_BASE_PTRS)
+
 #define SAMPLE_ACTION_DEPTH 3
 
 /**
@@ -82,7 +85,7 @@ struct datapath {
        struct flow_table __rcu *table;
 
        /* Switch ports. */
-       struct vport __rcu *ports[DP_MAX_PORTS];
+       struct flex_array ports;
        struct list_head port_list;
 
        /* Stats. */
@@ -159,6 +162,23 @@ static inline void ovs_dp_set_net(struct datapath *dp, 
struct net *net)
        write_pnet(&dp->net, net);
 }
 
+static inline struct vport *ovs_vport(const struct datapath *dp, int id)
+{
+       return flex_array_get_ptr(&dp->ports, id);
+}
+
+static inline struct vport *ovs_vport_check(const struct datapath *dp, int id)
+{
+       WARN_ON(!rcu_read_lock_held() && !rtnl_is_locked());
+       return ovs_vport(dp, id);
+}
+
+static inline struct vport *ovs_vport_protected(const struct datapath *dp, int 
id)
+{
+       ASSERT_RTNL();
+       return ovs_vport(dp, id);
+}
+
 extern struct notifier_block ovs_dp_device_notifier;
 extern struct genl_multicast_group ovs_dp_vport_multicast_group;
 extern int (*ovs_dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int 
cmd);
diff --git a/datapath/dp_sysfs_dp.c b/datapath/dp_sysfs_dp.c
index 2582321..aba1945 100644
--- a/datapath/dp_sysfs_dp.c
+++ b/datapath/dp_sysfs_dp.c
@@ -362,7 +362,7 @@ static struct attribute_group bridge_group = {
  */
 int ovs_dp_sysfs_add_dp(struct datapath *dp)
 {
-       struct vport *vport = rtnl_dereference(dp->ports[OVSP_LOCAL]);
+       struct vport *vport = ovs_vport_protected(dp, OVSP_LOCAL);
        struct kobject *kobj = vport->ops->get_kobj(vport);
        int err;
 
@@ -398,7 +398,7 @@ int ovs_dp_sysfs_add_dp(struct datapath *dp)
 
 int ovs_dp_sysfs_del_dp(struct datapath *dp)
 {
-       struct vport *vport = rtnl_dereference(dp->ports[OVSP_LOCAL]);
+       struct vport *vport = ovs_vport_protected(dp, OVSP_LOCAL);
        struct kobject *kobj = vport->ops->get_kobj(vport);
 
 #ifdef CONFIG_NET_NS
diff --git a/datapath/dp_sysfs_if.c b/datapath/dp_sysfs_if.c
index f564e98..a6cf61e 100644
--- a/datapath/dp_sysfs_if.c
+++ b/datapath/dp_sysfs_if.c
@@ -209,7 +209,7 @@ struct sysfs_ops ovs_brport_sysfs_ops = {
 int ovs_dp_sysfs_add_if(struct vport *p)
 {
        struct datapath *dp = p->dp;
-       struct vport *local_port = rtnl_dereference(dp->ports[OVSP_LOCAL]);
+       struct vport *local_port = ovs_vport_protected(dp, OVSP_LOCAL);
        struct brport_attribute **a;
        int err;
 
-- 
1.7.1

_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev

Reply via email to