X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=datapath%2Fdatapath.c;h=c8b50889a4937e9cb6dc64b683f07718dabd4ce0;hb=dab45bfd5748e7d871ae312df6e9b7ce89a8f46c;hp=dc91fecaca68cb66e7cec55366a16ec517302d71;hpb=c3827f619a38d3d202020838e1f92860046a3dbe;p=openvswitch diff --git a/datapath/datapath.c b/datapath/datapath.c index dc91feca..c8b50889 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -44,6 +44,7 @@ #include #include "openvswitch/datapath-protocol.h" +#include "checksum.h" #include "datapath.h" #include "actions.h" #include "flow.h" @@ -63,13 +64,13 @@ EXPORT_SYMBOL(dp_ioctl_hook); * dp_mutex nests inside the RTNL lock: if you need both you must take the RTNL * lock first. * - * It is safe to access the datapath and dp_port structures with just + * It is safe to access the datapath and vport structures with just * dp_mutex. */ static struct datapath *dps[ODP_MAX]; static DEFINE_MUTEX(dp_mutex); -static int new_dp_port(struct datapath *, struct odp_port *, int port_no); +static int new_vport(struct datapath *, struct odp_port *, int port_no); /* Must be called with rcu_read_lock or dp_mutex. */ struct datapath *get_dp(int dp_idx) @@ -95,7 +96,7 @@ static struct datapath *get_dp_locked(int dp_idx) /* Must be called with rcu_read_lock or RTNL lock. */ const char *dp_name(const struct datapath *dp) { - return vport_get_name(dp->ports[ODPP_LOCAL]->vport); + return vport_get_name(dp->ports[ODPP_LOCAL]); } static inline size_t br_nlmsg_size(void) @@ -110,12 +111,12 @@ static inline size_t br_nlmsg_size(void) } static int dp_fill_ifinfo(struct sk_buff *skb, - const struct dp_port *port, + const struct vport *port, int event, unsigned int flags) { const struct datapath *dp = port->dp; - int ifindex = vport_get_ifindex(port->vport); - int iflink = vport_get_iflink(port->vport); + int ifindex = vport_get_ifindex(port); + int iflink = vport_get_iflink(port); struct ifinfomsg *hdr; struct nlmsghdr *nlh; @@ -134,21 +135,20 @@ static int dp_fill_ifinfo(struct sk_buff *skb, hdr->__ifi_pad = 0; hdr->ifi_type = ARPHRD_ETHER; hdr->ifi_index = ifindex; - hdr->ifi_flags = vport_get_flags(port->vport); + hdr->ifi_flags = vport_get_flags(port); hdr->ifi_change = 0; - NLA_PUT_STRING(skb, IFLA_IFNAME, vport_get_name(port->vport)); - NLA_PUT_U32(skb, IFLA_MASTER, vport_get_ifindex(dp->ports[ODPP_LOCAL]->vport)); - NLA_PUT_U32(skb, IFLA_MTU, vport_get_mtu(port->vport)); + NLA_PUT_STRING(skb, IFLA_IFNAME, vport_get_name(port)); + NLA_PUT_U32(skb, IFLA_MASTER, vport_get_ifindex(dp->ports[ODPP_LOCAL])); + NLA_PUT_U32(skb, IFLA_MTU, vport_get_mtu(port)); #ifdef IFLA_OPERSTATE NLA_PUT_U8(skb, IFLA_OPERSTATE, - vport_is_running(port->vport) - ? vport_get_operstate(port->vport) + vport_is_running(port) + ? vport_get_operstate(port) : IF_OPER_DOWN); #endif - NLA_PUT(skb, IFLA_ADDRESS, ETH_ALEN, - vport_get_addr(port->vport)); + NLA_PUT(skb, IFLA_ADDRESS, ETH_ALEN, vport_get_addr(port)); if (ifindex != iflink) NLA_PUT_U32(skb, IFLA_LINK,iflink); @@ -160,7 +160,7 @@ nla_put_failure: return -EMSGSIZE; } -static void dp_ifinfo_notify(int event, struct dp_port *port) +static void dp_ifinfo_notify(int event, struct vport *port) { struct sk_buff *skb; int err = -ENOBUFS; @@ -253,7 +253,7 @@ static int create_dp(int dp_idx, const char __user *devnamep) BUILD_BUG_ON(sizeof(internal_dev_port.devname) != sizeof(devname)); strcpy(internal_dev_port.devname, devname); strcpy(internal_dev_port.type, "internal"); - err = new_dp_port(dp, &internal_dev_port, ODPP_LOCAL); + err = new_vport(dp, &internal_dev_port, ODPP_LOCAL); if (err) { if (err == -EBUSY) err = -EEXIST; @@ -291,7 +291,7 @@ err: static void do_destroy_dp(struct datapath *dp) { - struct dp_port *p, *n; + struct vport *p, *n; int i; list_for_each_entry_safe (p, n, &dp->port_list, node) @@ -334,30 +334,17 @@ err_unlock: return err; } -static void release_dp_port(struct kobject *kobj) -{ - struct dp_port *p = container_of(kobj, struct dp_port, kobj); - kfree(p); -} - -static struct kobj_type brport_ktype = { -#ifdef CONFIG_SYSFS - .sysfs_ops = &brport_sysfs_ops, -#endif - .release = release_dp_port -}; - /* Called with RTNL lock and dp_mutex. */ -static int new_dp_port(struct datapath *dp, struct odp_port *odp_port, int port_no) +static int new_vport(struct datapath *dp, struct odp_port *odp_port, int port_no) { struct vport_parms parms; struct vport *vport; - struct dp_port *p; - int err; parms.name = odp_port->devname; parms.type = odp_port->type; parms.config = odp_port->config; + parms.dp = dp; + parms.port_no = port_no; vport_lock(); vport = vport_add(&parms); @@ -366,31 +353,11 @@ static int new_dp_port(struct datapath *dp, struct odp_port *odp_port, int port_ if (IS_ERR(vport)) return PTR_ERR(vport); - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return -ENOMEM; - - p->port_no = port_no; - p->dp = dp; - p->vport = vport; - atomic_set(&p->sflow_pool, 0); - - err = vport_attach(vport, p); - if (err) { - kfree(p); - return err; - } - - rcu_assign_pointer(dp->ports[port_no], p); - list_add_rcu(&p->node, &dp->port_list); + rcu_assign_pointer(dp->ports[port_no], vport); + list_add_rcu(&vport->node, &dp->port_list); dp->n_ports++; - /* Initialize kobject for bridge. This will be added as - * /sys/class/net//brport later, if sysfs is enabled. */ - p->kobj.kset = NULL; - kobject_init(&p->kobj, &brport_ktype); - - dp_ifinfo_notify(RTM_NEWLINK, p); + dp_ifinfo_notify(RTM_NEWLINK, vport); return 0; } @@ -421,7 +388,7 @@ static int attach_port(int dp_idx, struct odp_port __user *portp) goto out_unlock_dp; got_port_no: - err = new_dp_port(dp, &port, port_no); + err = new_vport(dp, &port, port_no); if (err) goto out_unlock_dp; @@ -438,9 +405,8 @@ out: return err; } -int dp_detach_port(struct dp_port *p) +int dp_detach_port(struct vport *p) { - struct vport *vport = p->vport; int err; ASSERT_RTNL(); @@ -454,25 +420,17 @@ int dp_detach_port(struct dp_port *p) list_del_rcu(&p->node); rcu_assign_pointer(p->dp->ports[p->port_no], NULL); - err = vport_detach(vport); - if (err) - return err; - - /* Then wait until no one is still using it, and destroy it. */ - synchronize_rcu(); - + /* Then destroy it. */ vport_lock(); - vport_del(vport); + err = vport_del(p); vport_unlock(); - kobject_put(&p->kobj); - - return 0; + return err; } static int detach_port(int dp_idx, int port_no) { - struct dp_port *p; + struct vport *p; struct datapath *dp; int err; @@ -502,7 +460,7 @@ out: } /* Must be called with rcu_read_lock. */ -void dp_process_received_packet(struct dp_port *p, struct sk_buff *skb) +void dp_process_received_packet(struct vport *p, struct sk_buff *skb) { struct datapath *dp = p->dp; struct dp_stats_percpu *stats; @@ -511,7 +469,7 @@ void dp_process_received_packet(struct dp_port *p, struct sk_buff *skb) struct loop_counter *loop; int error; - OVS_CB(skb)->dp_port = p; + OVS_CB(skb)->vport = p; if (!OVS_CB(skb)->flow) { struct odp_flow_key key; @@ -583,171 +541,6 @@ out: local_bh_enable(); } -#if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID) -/* This code is based on skb_checksum_setup() from Xen's net/dev/core.c. We - * can't call this function directly because it isn't exported in all - * versions. */ -int vswitch_skb_checksum_setup(struct sk_buff *skb) -{ - 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 (!pskb_may_pull(skb, skb_network_header(skb) + sizeof(struct iphdr) - skb->data)) - 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()) - pr_err("Attempting to checksum a non-TCP/UDP packet, " - "dropping a protocol %d packet", - iph->protocol); - goto out; - } - - if (!pskb_may_pull(skb, th + csum_offset + 2 - skb->data)) - 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 err; -} -#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 - * (though not verified) checksum in packet but not in skb->csum. Packets - * from the bridge local port will also have this type. - * 2. CHECKSUM_COMPLETE (CHECKSUM_HW): Good device that computes checksums, - * also the GRE module. This is the same as CHECKSUM_NONE, except it has - * a valid skb->csum. Importantly, both contain a full checksum (not - * verified) in the packet itself. The only difference is that if the - * packet gets to L4 processing on this machine (not in DomU) we won't - * have to recompute the checksum to verify. Most hardware devices do not - * produce packets with this type, even if they support receive checksum - * offloading (they produce type #5). - * 3. CHECKSUM_PARTIAL (CHECKSUM_HW): Packet without full checksum and needs to - * be computed if it is sent off box. Unfortunately on earlier kernels, - * this case is impossible to distinguish from #2, despite having opposite - * meanings. Xen adds an extra field on earlier kernels (see #4) in order - * to distinguish the different states. - * 4. CHECKSUM_UNNECESSARY (with proto_csum_blank true): This packet was - * generated locally by a Xen DomU and has a partial checksum. If it is - * handled on this machine (Dom0 or DomU), then the checksum will not be - * computed. If it goes off box, the checksum in the packet needs to be - * completed. Calling skb_checksum_setup converts this to CHECKSUM_HW - * (CHECKSUM_PARTIAL) so that the checksum can be completed. In later - * kernels, this combination is replaced with CHECKSUM_PARTIAL. - * 5. CHECKSUM_UNNECESSARY (with proto_csum_blank false): Packet with a correct - * full checksum or using a protocol without a checksum. skb->csum is - * undefined. This is common from devices with receive checksum - * offloading. This is somewhat similar to CHECKSUM_NONE, except that - * nobody will try to verify the checksum with CHECKSUM_UNNECESSARY. - * - * Note that on earlier kernels, CHECKSUM_COMPLETE and CHECKSUM_PARTIAL are - * both defined as CHECKSUM_HW. Normally the meaning of CHECKSUM_HW is clear - * based on whether it is on the transmit or receive path. After the datapath - * it will be intepreted as CHECKSUM_PARTIAL. If the packet already has a - * checksum, we will panic. Since we can receive packets with checksums, we - * assume that all CHECKSUM_HW packets have checksums and map them to - * CHECKSUM_NONE, which has a similar meaning (the it is only different if the - * packet is processed by the local IP stack, in which case it will need to - * be reverified). If we receive a packet with CHECKSUM_HW that really means - * CHECKSUM_PARTIAL, it will be sent with the wrong checksum. However, there - * shouldn't be any devices that do this with bridging. */ -void compute_ip_summed(struct sk_buff *skb, bool xmit) -{ - /* For our convenience these defines change repeatedly between kernel - * versions, so we can't just copy them over... */ - switch (skb->ip_summed) { - case CHECKSUM_NONE: - OVS_CB(skb)->ip_summed = OVS_CSUM_NONE; - break; - case CHECKSUM_UNNECESSARY: - OVS_CB(skb)->ip_summed = OVS_CSUM_UNNECESSARY; - break; -#ifdef CHECKSUM_HW - /* In theory this could be either CHECKSUM_PARTIAL or CHECKSUM_COMPLETE. - * However, on the receive side we should only get CHECKSUM_PARTIAL - * packets from Xen, which uses some special fields to represent this - * (see below). Since we can only make one type work, pick the one - * that actually happens in practice. - * - * On the transmit side (basically after skb_checksum_setup() - * has been run or on internal dev transmit), packets with - * CHECKSUM_COMPLETE aren't generated, so assume CHECKSUM_PARTIAL. */ - case CHECKSUM_HW: - if (!xmit) - OVS_CB(skb)->ip_summed = OVS_CSUM_COMPLETE; - else - OVS_CB(skb)->ip_summed = OVS_CSUM_PARTIAL; - - break; -#else - case CHECKSUM_COMPLETE: - OVS_CB(skb)->ip_summed = OVS_CSUM_COMPLETE; - break; - case CHECKSUM_PARTIAL: - OVS_CB(skb)->ip_summed = OVS_CSUM_PARTIAL; - break; -#endif - default: - pr_err("unknown checksum type %d\n", skb->ip_summed); - /* None seems the safest... */ - OVS_CB(skb)->ip_summed = OVS_CSUM_NONE; - } - -#if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID) - /* Xen has a special way of representing CHECKSUM_PARTIAL on older - * kernels. It should not be set on the transmit path though. */ - if (skb->proto_csum_blank) - OVS_CB(skb)->ip_summed = OVS_CSUM_PARTIAL; - - WARN_ON_ONCE(skb->proto_csum_blank && xmit); -#endif -} - -/* This function closely resembles skb_forward_csum() used by the bridge. It - * is slightly different because we are only concerned with bridging and not - * other types of forwarding and can get away with slightly more optimal - * behavior.*/ -void forward_ip_summed(struct sk_buff *skb) -{ -#ifdef CHECKSUM_HW - if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE) - skb->ip_summed = CHECKSUM_NONE; -#endif -} - /* Append each packet in 'skb' list to 'queue'. There will be only one packet * unless we broke up a GSO packet. */ static int queue_control_packets(struct sk_buff *skb, struct sk_buff_head *queue, @@ -757,8 +550,8 @@ static int queue_control_packets(struct sk_buff *skb, struct sk_buff_head *queue int port_no; int err; - if (OVS_CB(skb)->dp_port) - port_no = OVS_CB(skb)->dp_port->port_no; + if (OVS_CB(skb)->vport) + port_no = OVS_CB(skb)->vport->port_no; else port_no = ODPP_LOCAL; @@ -1382,7 +1175,7 @@ static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp) /* MTU of the dp pseudo-device: ETH_DATA_LEN or the minimum of the ports */ int dp_min_mtu(const struct datapath *dp) { - struct dp_port *p; + struct vport *p; int mtu = 0; ASSERT_RTNL(); @@ -1392,10 +1185,10 @@ int dp_min_mtu(const struct datapath *dp) /* Skip any internal ports, since that's what we're trying to * set. */ - if (is_internal_vport(p->vport)) + if (is_internal_vport(p)) continue; - dev_mtu = vport_get_mtu(p->vport); + dev_mtu = vport_get_mtu(p); if (!mtu || dev_mtu < mtu) mtu = dev_mtu; } @@ -1407,7 +1200,7 @@ int dp_min_mtu(const struct datapath *dp) * be called with RTNL lock. */ void set_internal_devs_mtu(const struct datapath *dp) { - struct dp_port *p; + struct vport *p; int mtu; ASSERT_RTNL(); @@ -1415,20 +1208,20 @@ void set_internal_devs_mtu(const struct datapath *dp) mtu = dp_min_mtu(dp); list_for_each_entry_rcu (p, &dp->port_list, node) { - if (is_internal_vport(p->vport)) - vport_set_mtu(p->vport, mtu); + if (is_internal_vport(p)) + vport_set_mtu(p, mtu); } } -static int put_port(const struct dp_port *p, struct odp_port __user *uop) +static int put_port(const struct vport *p, struct odp_port __user *uop) { struct odp_port op; memset(&op, 0, sizeof op); rcu_read_lock(); - strncpy(op.devname, vport_get_name(p->vport), sizeof op.devname); - strncpy(op.type, vport_get_type(p->vport), sizeof op.type); + strncpy(op.devname, vport_get_name(p), sizeof op.devname); + strncpy(op.type, vport_get_type(p), sizeof op.type); rcu_read_unlock(); op.port = p->port_no; @@ -1445,7 +1238,6 @@ static int query_port(struct datapath *dp, struct odp_port __user *uport) if (port.devname[0]) { struct vport *vport; - struct dp_port *dp_port; int err = 0; port.devname[IFNAMSIZ - 1] = '\0'; @@ -1458,14 +1250,12 @@ static int query_port(struct datapath *dp, struct odp_port __user *uport) err = -ENODEV; goto error_unlock; } - - dp_port = vport_get_dp_port(vport); - if (!dp_port || dp_port->dp != dp) { + if (vport->dp != dp) { err = -ENOENT; goto error_unlock; } - port.port = dp_port->port_no; + port.port = vport->port_no; error_unlock: rcu_read_unlock(); @@ -1488,7 +1278,7 @@ static int do_list_ports(struct datapath *dp, struct odp_port __user *uports, { int idx = 0; if (n_ports) { - struct dp_port *p; + struct vport *p; list_for_each_entry_rcu (p, &dp->port_list, node) { if (put_port(p, &uports[idx])) @@ -2097,15 +1887,9 @@ success: if (skb->ip_summed == CHECKSUM_PARTIAL) { if (copy_bytes == skb->len) { __wsum csum = 0; - unsigned int csum_start, csum_offset; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) - csum_start = skb->csum_start - skb_headroom(skb); - csum_offset = skb->csum_offset; -#else - csum_start = skb_transport_header(skb) - skb->data; - csum_offset = skb->csum; -#endif + u16 csum_start, csum_offset; + + get_skb_csum_pointers(skb, &csum_start, &csum_offset); BUG_ON(csum_start >= skb_headlen(skb)); retval = skb_copy_and_csum_datagram(skb, csum_start, buf + csum_start, copy_bytes - csum_start, &csum);