Merge commit 'origin/citrix'
[sliver-openvswitch.git] / datapath / datapath.c
index 5e1a352..e0b03d9 100644 (file)
 int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
 EXPORT_SYMBOL(dp_ioctl_hook);
 
-int (*dp_add_dp_hook)(struct datapath *dp);
-EXPORT_SYMBOL(dp_add_dp_hook);
-
-int (*dp_del_dp_hook)(struct datapath *dp);
-EXPORT_SYMBOL(dp_del_dp_hook);
-
-int (*dp_add_if_hook)(struct net_bridge_port *p);
-EXPORT_SYMBOL(dp_add_if_hook);
-
-int (*dp_del_if_hook)(struct net_bridge_port *p);
-EXPORT_SYMBOL(dp_del_if_hook);
-
 /* Datapaths.  Protected on the read side by rcu_read_lock, on the write side
  * by dp_mutex.  dp_mutex is almost completely redundant with genl_mutex
  * maintained by the Generic Netlink code, but the timeout path needs mutual
@@ -177,12 +165,23 @@ static void dp_ifinfo_notify(int event, struct net_bridge_port *port)
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       return;
 errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_LINK, err);
 }
 
+static void release_dp(struct kobject *kobj)
+{
+       struct datapath *dp = container_of(kobj, struct datapath, ifobj);
+       kfree(dp);
+}
+
+struct kobj_type dp_ktype = {
+       .release = release_dp
+};
+
 static int create_dp(int dp_idx, const char __user *devnamep)
 {
        struct net_device *dp_dev;
@@ -224,6 +223,11 @@ static int create_dp(int dp_idx, const char __user *devnamep)
                skb_queue_head_init(&dp->queues[i]);
        init_waitqueue_head(&dp->waitqueue);
 
+       /* Initialize kobject for bridge.  This will be added as
+        * /sys/class/net/<devname>/brif later, if sysfs is enabled. */
+       dp->ifobj.kset = NULL;
+       kobject_init(&dp->ifobj, &dp_ktype);
+
        /* Allocate table. */
        err = -ENOMEM;
        rcu_assign_pointer(dp->table, dp_table_create(DP_L1_SIZE));
@@ -251,8 +255,7 @@ static int create_dp(int dp_idx, const char __user *devnamep)
        mutex_unlock(&dp_mutex);
        rtnl_unlock();
 
-       if (dp_add_dp_hook)
-               dp_add_dp_hook(dp);
+       dp_sysfs_add_dp(dp);
 
        return 0;
 
@@ -280,8 +283,7 @@ static void do_destroy_dp(struct datapath *dp)
                if (p->port_no != ODPP_LOCAL)
                        dp_del_port(p);
 
-       if (dp_del_dp_hook)
-               dp_del_dp_hook(dp);
+       dp_sysfs_del_dp(dp);
 
        rcu_assign_pointer(dps[dp->dp_idx], NULL);
 
@@ -294,7 +296,7 @@ static void do_destroy_dp(struct datapath *dp)
        for (i = 0; i < DP_MAX_GROUPS; i++)
                kfree(dp->groups[i]);
        free_percpu(dp->stats_percpu);
-       kfree(dp);
+       kobject_put(&dp->ifobj);
        module_put(THIS_MODULE);
 }
 
@@ -319,6 +321,19 @@ err_unlock:
        return err;
 }
 
+static void release_nbp(struct kobject *kobj)
+{
+       struct net_bridge_port *p = container_of(kobj, struct net_bridge_port, kobj);
+       kfree(p);
+}
+
+struct kobj_type brport_ktype = {
+#ifdef CONFIG_SYSFS
+       .sysfs_ops = &brport_sysfs_ops,
+#endif
+       .release = release_nbp
+};
+
 /* Called with RTNL lock and dp_mutex. */
 static int new_nbp(struct datapath *dp, struct net_device *dev, int port_no)
 {
@@ -348,6 +363,11 @@ static int new_nbp(struct datapath *dp, struct net_device *dev, int port_no)
        list_add_rcu(&p->node, &dp->port_list);
        dp->n_ports++;
 
+       /* Initialize kobject for bridge.  This will be added as
+        * /sys/class/net/<devname>/brport later, if sysfs is enabled. */
+       p->kobj.kset = NULL;
+       kobject_init(&p->kobj, &brport_ktype);
+
        dp_ifinfo_notify(RTM_NEWLINK, p);
 
        return 0;
@@ -365,11 +385,6 @@ static int add_port(int dp_idx, struct odp_port __user *portp)
        if (copy_from_user(&port, portp, sizeof port))
                goto out;
        port.devname[IFNAMSIZ - 1] = '\0';
-       port_no = port.port;
-
-       err = -EINVAL;
-       if (port_no < 0 || port_no >= DP_MAX_PORTS)
-               goto out;
 
        rtnl_lock();
        dp = get_dp_locked(dp_idx);
@@ -377,10 +392,13 @@ static int add_port(int dp_idx, struct odp_port __user *portp)
        if (!dp)
                goto out_unlock_rtnl;
 
-       err = -EEXIST;
-       if (dp->ports[port_no])
-               goto out_unlock_dp;
+       for (port_no = 1; port_no < DP_MAX_PORTS; port_no++)
+               if (!dp->ports[port_no])
+                       goto got_port_no;
+       err = -EXFULL;
+       goto out_unlock_dp;
 
+got_port_no:
        if (!(port.flags & ODP_PORT_INTERNAL)) {
                err = -ENODEV;
                dev = dev_get_by_name(&init_net, port.devname);
@@ -403,8 +421,9 @@ static int add_port(int dp_idx, struct odp_port __user *portp)
        if (err)
                goto out_put;
 
-       if (dp_add_if_hook)
-               dp_add_if_hook(dp->ports[port_no]);
+       dp_sysfs_add_if(dp->ports[port_no]);
+
+       err = __put_user(port_no, &port.port);
 
 out_put:
        dev_put(dev);
@@ -420,10 +439,8 @@ int dp_del_port(struct net_bridge_port *p)
 {
        ASSERT_RTNL();
 
-#ifdef SUPPORT_SYSFS
-       if (p->port_no != ODPP_LOCAL && dp_del_if_hook)
-               sysfs_remove_link(&p->dp->ifobj, p->dev->name);
-#endif
+       if (p->port_no != ODPP_LOCAL)
+               dp_sysfs_del_if(p);
        dp_ifinfo_notify(RTM_DELLINK, p);
 
        p->dp->n_ports--;
@@ -444,15 +461,10 @@ int dp_del_port(struct net_bridge_port *p)
        /* Then wait until no one is still using it, and destroy it. */
        synchronize_rcu();
 
-       if (is_dp_dev(p->dev)) {
+       if (is_dp_dev(p->dev))
                dp_dev_destroy(p->dev);
-       }
-       if (p->port_no != ODPP_LOCAL && dp_del_if_hook) {
-               dp_del_if_hook(p);
-       } else {
-               dev_put(p->dev);
-               kfree(p);
-       }
+       dev_put(p->dev);
+       kobject_put(&p->kobj);
 
        return 0;
 }
@@ -564,7 +576,7 @@ static int dp_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
 #error
 #endif
 
-#ifdef CONFIG_XEN
+#if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
 /* This code is copied verbatim from net/dev/core.c in Xen's
  * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644.  We can't call those functions
  * directly because they aren't exported. */
@@ -580,7 +592,7 @@ static int skb_pull_up_to(struct sk_buff *skb, void *ptr)
        }
 }
 
-int skb_checksum_setup(struct sk_buff *skb)
+int vswitch_skb_checksum_setup(struct sk_buff *skb)
 {
        if (skb->proto_csum_blank) {
                if (skb->protocol != htons(ETH_P_IP))
@@ -611,7 +623,9 @@ int skb_checksum_setup(struct sk_buff *skb)
 out:
        return -EPROTO;
 }
-#endif
+#else
+int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; }
+#endif /* CONFIG_XEN && linux == 2.6.18 */
 
 int
 dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
@@ -637,7 +651,7 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
         * the non-Xen case, but it is difficult to trigger or test this case
         * there, hence the WARN_ON_ONCE().
         */
-       err = skb_checksum_setup(skb);
+       err = vswitch_skb_checksum_setup(skb);
        if (err)
                goto err_kfree_skb;
 #ifndef CHECKSUM_HW
@@ -813,6 +827,7 @@ static void get_stats(struct sw_flow *flow, struct odp_flow_stats *stats)
        stats->n_bytes = flow->byte_count;
        stats->ip_tos = flow->ip_tos;
        stats->tcp_flags = flow->tcp_flags;
+       stats->error = 0;
 }
 
 static void clear_stats(struct sw_flow *flow)
@@ -842,7 +857,7 @@ retry:
        bucket = dp_table_lookup_for_insert(table, &uf.flow.key);
        if (!bucket) {
                /* No such flow, and the slots where it could go are full. */
-               error = uf.flags & ODPPF_CREATE ? -EXFULL : -ENOENT;
+               error = uf.flags & ODPPF_CREATE ? -EFBIG : -ENOENT;
                goto error;
        } else if (!*bucket) {
                /* No such flow, but we found an available slot for it. */
@@ -941,8 +956,6 @@ static int put_actions(const struct sw_flow *flow, struct odp_flow __user *ufp)
 
        if (!n_actions)
                return 0;
-       if (ufp->n_actions > INT_MAX / sizeof(union odp_action))
-               return -EINVAL;
 
        sf_acts = rcu_dereference(flow->sf_acts);
        if (__put_user(sf_acts->n_actions, &ufp->n_actions) ||
@@ -968,9 +981,7 @@ static int answer_query(struct sw_flow *flow, struct odp_flow __user *ufp)
        return put_actions(flow, ufp);
 }
 
-static int del_or_query_flow(struct datapath *dp,
-                            struct odp_flow __user *ufp,
-                            unsigned int cmd)
+static int del_flow(struct datapath *dp, struct odp_flow __user *ufp)
 {
        struct dp_table *table = rcu_dereference(dp->table);
        struct odp_flow uf;
@@ -987,29 +998,24 @@ static int del_or_query_flow(struct datapath *dp,
        if (!flow)
                goto error;
 
-       if (cmd == ODP_FLOW_DEL) {
-               /* XXX redundant lookup */
-               error = dp_table_delete(table, flow);
-               if (error)
-                       goto error;
+       /* XXX redundant lookup */
+       error = dp_table_delete(table, flow);
+       if (error)
+               goto error;
 
-               /* XXX These statistics might lose a few packets, since other
-                * CPUs can be using this flow.  We used to synchronize_rcu()
-                * to make sure that we get completely accurate stats, but that
-                * blows our performance, badly. */
-               dp->n_flows--;
-               error = answer_query(flow, ufp);
-               flow_deferred_free(flow);
-       } else {
-               error = answer_query(flow, ufp);
-       }
+       /* XXX These statistics might lose a few packets, since other CPUs can
+        * be using this flow.  We used to synchronize_rcu() to make sure that
+        * we get completely accurate stats, but that blows our performance,
+        * badly. */
+       dp->n_flows--;
+       error = answer_query(flow, ufp);
+       flow_deferred_free(flow);
 
 error:
        return error;
 }
 
-static int query_multiple_flows(struct datapath *dp,
-                               const struct odp_flowvec *flowvec)
+static int query_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
 {
        struct dp_table *table = rcu_dereference(dp->table);
        int i;
@@ -1025,7 +1031,7 @@ static int query_multiple_flows(struct datapath *dp,
 
                flow = dp_table_lookup(table, &uf.key);
                if (!flow)
-                       error = __clear_user(&ufp->stats, sizeof ufp->stats);
+                       error = __put_user(ENOENT, &ufp->stats.error);
                else
                        error = answer_query(flow, ufp);
                if (error)
@@ -1158,8 +1164,7 @@ error:
        return err;
 }
 
-static int
-get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
+static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
 {
        struct odp_stats stats;
        int i;
@@ -1184,6 +1189,29 @@ get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
        return copy_to_user(statsp, &stats, sizeof stats) ? -EFAULT : 0;
 }
 
+/* MTU of the dp pseudo-device: ETH_DATA_LEN or the minimum of the ports */
+int dp_min_mtu(const struct datapath *dp)
+{
+       struct net_bridge_port *p;
+       int mtu = 0;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry_rcu (p, &dp->port_list, node) {
+               struct net_device *dev = p->dev;
+
+               /* Skip any internal ports, since that's what we're trying to
+                * set. */
+               if (is_dp_dev(dev))
+                       continue;
+
+               if (!mtu || dev->mtu < mtu)
+                       mtu = dev->mtu;
+       }
+
+       return mtu ? mtu : ETH_DATA_LEN;
+}
+
 static int
 put_port(const struct net_bridge_port *p, struct odp_port __user *uop)
 {
@@ -1251,7 +1279,7 @@ list_ports(struct datapath *dp, struct odp_portvec __user *pvp)
                                break;
                }
        }
-       return put_user(idx, &pvp->n_ports);
+       return put_user(dp->n_ports, &pvp->n_ports);
 }
 
 /* RCU callback for freeing a dp_port_group */
@@ -1335,24 +1363,28 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
        /* Handle commands with special locking requirements up front. */
        switch (cmd) {
        case ODP_DP_CREATE:
-               return create_dp(dp_idx, (char __user *)argp);
+               err = create_dp(dp_idx, (char __user *)argp);
+               goto exit;
 
        case ODP_DP_DESTROY:
-               return destroy_dp(dp_idx);
+               err = destroy_dp(dp_idx);
+               goto exit;
 
        case ODP_PORT_ADD:
-               return add_port(dp_idx, (struct odp_port __user *)argp);
+               err = add_port(dp_idx, (struct odp_port __user *)argp);
+               goto exit;
 
        case ODP_PORT_DEL:
                err = get_user(port_no, (int __user *)argp);
-               if (err)
-                       break;
-               return del_port(dp_idx, port_no);
+               if (!err)
+                       err = del_port(dp_idx, port_no);
+               goto exit;
        }
 
        dp = get_dp_locked(dp_idx);
+       err = -ENODEV;
        if (!dp)
-               return -ENODEV;
+               goto exit;
 
        switch (cmd) {
        case ODP_DP_STATS:
@@ -1414,13 +1446,11 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                break;
 
        case ODP_FLOW_DEL:
-       case ODP_FLOW_GET:
-               err = del_or_query_flow(dp, (struct odp_flow __user *)argp,
-                                       cmd);
+               err = del_flow(dp, (struct odp_flow __user *)argp);
                break;
 
-       case ODP_FLOW_GET_MULTIPLE:
-               err = do_flowvec_ioctl(dp, argp, query_multiple_flows);
+       case ODP_FLOW_GET:
+               err = do_flowvec_ioctl(dp, argp, query_flows);
                break;
 
        case ODP_FLOW_LIST:
@@ -1436,6 +1466,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                break;
        }
        mutex_unlock(&dp->mutex);
+exit:
        return err;
 }