Merge branch 'master' into next
[sliver-openvswitch.git] / datapath / datapath.c
index 08b6200..6a3b9ec 100644 (file)
@@ -349,6 +349,7 @@ static int new_nbp(struct datapath *dp, struct net_device *dev, int port_no)
        p->port_no = port_no;
        p->dp = dp;
        p->dev = dev;
+       atomic_set(&p->sflow_pool, 0);
        if (!is_dp_dev(dev))
                rcu_assign_pointer(dev->br_port, p);
        else {
@@ -419,6 +420,7 @@ got_port_no:
        if (err)
                goto out_put;
 
+       set_dp_devs_mtu(dp, dev);
        dp_sysfs_add_if(dp->ports[port_no]);
 
        err = __put_user(port_no, &port.port);
@@ -575,9 +577,10 @@ static int dp_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
 #endif
 
 #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
-/* 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. */
+/* This code is based on a skb_checksum_setup from net/dev/core.c from a
+ * combination of Lenny's 2.6.26 Xen kernel and Xen's
+ * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644.  We can't call this function
+ * directly because it isn't exported in all versions. */
 static int skb_pull_up_to(struct sk_buff *skb, void *ptr)
 {
        if (ptr < (void *)skb->tail)
@@ -592,38 +595,59 @@ static int skb_pull_up_to(struct sk_buff *skb, void *ptr)
 
 int vswitch_skb_checksum_setup(struct sk_buff *skb)
 {
-       if (skb->proto_csum_blank) {
-               if (skb->protocol != htons(ETH_P_IP))
-                       goto out;
-               if (!skb_pull_up_to(skb, skb->nh.iph + 1))
-                       goto out;
-               skb->h.raw = (unsigned char *)skb->nh.iph + 4*skb->nh.iph->ihl;
-               switch (skb->nh.iph->protocol) {
-               case IPPROTO_TCP:
-                       skb->csum = offsetof(struct tcphdr, check);
-                       break;
-               case IPPROTO_UDP:
-                       skb->csum = offsetof(struct udphdr, check);
-                       break;
-               default:
-                       if (net_ratelimit())
-                               printk(KERN_ERR "Attempting to checksum a non-"
-                                      "TCP/UDP packet, dropping a protocol"
-                                      " %d packet", skb->nh.iph->protocol);
-                       goto out;
-               }
-               if (!skb_pull_up_to(skb, skb->h.raw + skb->csum + 2))
-                       goto out;
-               skb->ip_summed = CHECKSUM_HW;
-               skb->proto_csum_blank = 0;
+       struct iphdr *iph;
+       unsigned char *th;
+       int err = -EPROTO;
+       __u16 csum_start, csum_offset;
+
+       if (!skb->proto_csum_blank)
+               return 0;
+
+       if (skb->protocol != htons(ETH_P_IP))
+               goto out;
+
+       if (!skb_pull_up_to(skb, skb_network_header(skb) + 1))
+               goto out;
+
+       iph = ip_hdr(skb);
+       th = skb_network_header(skb) + 4 * iph->ihl;
+
+       csum_start = th - skb->head;
+       switch (iph->protocol) {
+       case IPPROTO_TCP:
+               csum_offset = offsetof(struct tcphdr, check);
+               break;
+       case IPPROTO_UDP:
+               csum_offset = offsetof(struct udphdr, check);
+               break;
+       default:
+               if (net_ratelimit())
+                       printk(KERN_ERR "Attempting to checksum a non-"
+                              "TCP/UDP packet, dropping a protocol"
+                              " %d packet", iph->protocol);
+               goto out;
        }
-       return 0;
+
+       if (!skb_pull_up_to(skb, th + csum_offset + 2))
+               goto out;
+
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       skb->proto_csum_blank = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+       skb->csum_start = csum_start;
+       skb->csum_offset = csum_offset;
+#else
+       skb_set_transport_header(skb, csum_start - skb_headroom(skb));
+       skb->csum = csum_offset;
+#endif
+
+       err = 0;
+
 out:
-       return -EPROTO;
+       return err;
 }
-#else
-int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; }
-#endif /* CONFIG_XEN && linux == 2.6.18 */
+#endif /* CONFIG_XEN && HAVE_PROTO_DATA_VALID */
 
  /* Types of checksums that we can receive (these all refer to L4 checksums):
  * 1. CHECKSUM_NONE: Device that did not compute checksum, contains full
@@ -771,8 +795,7 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
        int err;
 
        WARN_ON_ONCE(skb_shared(skb));
-       BUG_ON(queue_no != _ODPL_MISS_NR && queue_no != _ODPL_ACTION_NR);
-
+       BUG_ON(queue_no != _ODPL_MISS_NR && queue_no != _ODPL_ACTION_NR && queue_no != _ODPL_SFLOW_NR);
        queue = &dp->queues[queue_no];
        err = -ENOBUFS;
        if (skb_queue_len(queue) >= DP_MAX_QUEUE_LEN)
@@ -1295,6 +1318,29 @@ int dp_min_mtu(const struct datapath *dp)
        return mtu ? mtu : ETH_DATA_LEN;
 }
 
+/* Sets the MTU of all datapath devices to the minimum of the ports. 'dev'
+ * is the device whose MTU may have changed.  Must be called with RTNL lock
+ * and dp_mutex. */
+void set_dp_devs_mtu(const struct datapath *dp, struct net_device *dev)
+{
+       struct net_bridge_port *p;
+       int mtu;
+
+       ASSERT_RTNL();
+
+       if (is_dp_dev(dev))
+               return;
+
+       mtu = dp_min_mtu(dp);
+
+       list_for_each_entry_rcu (p, &dp->port_list, node) {
+               struct net_device *br_dev = p->dev;
+
+               if (is_dp_dev(br_dev))
+                       dev_set_mtu(br_dev, mtu);
+       }
+}
+
 static int
 put_port(const struct net_bridge_port *p, struct odp_port __user *uop)
 {
@@ -1451,6 +1497,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
        int dp_idx = iminor(f->f_dentry->d_inode);
        struct datapath *dp;
        int drop_frags, listeners, port_no;
+       unsigned int sflow_probability;
        int err;
 
        /* Handle commands with special locking requirements up front. */
@@ -1514,6 +1561,16 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                set_listen_mask(f, listeners);
                break;
 
+       case ODP_GET_SFLOW_PROBABILITY:
+               err = put_user(dp->sflow_probability, (unsigned int __user *)argp);
+               break;
+
+       case ODP_SET_SFLOW_PROBABILITY:
+               err = get_user(sflow_probability, (unsigned int __user *)argp);
+               if (!err)
+                       dp->sflow_probability = sflow_probability;
+               break;
+
        case ODP_PORT_QUERY:
                err = query_port(dp, (struct odp_port __user *)argp);
                break;