X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=datapath%2Fdatapath.c;h=7a70163a0a35912505a55afa05f0cfafb5625d82;hb=cdee00fd635d1e0f1eeb5d9c009daeb59abd4777;hp=46497c690a446a6a27418aba9d5b9ca92c501d1d;hpb=a01ef04ced0d3e04e7d5e5038ef76c861b5c1da3;p=openvswitch diff --git a/datapath/datapath.c b/datapath/datapath.c index 46497c69..7a70163a 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -44,16 +44,17 @@ #include #include "openvswitch/datapath-protocol.h" +#include "checksum.h" #include "datapath.h" #include "actions.h" #include "flow.h" +#include "loop_counter.h" #include "odp-compat.h" #include "table.h" #include "vport-internal_dev.h" #include "compat.h" - int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd); EXPORT_SYMBOL(dp_ioctl_hook); @@ -63,30 +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); -/* We limit the number of times that we pass into dp_process_received_packet() - * to avoid blowing out the stack in the event that we have a loop. */ -struct loop_counter { - int count; /* Count. */ - bool looping; /* Loop detected? */ -}; - -#define DP_MAX_LOOPS 5 - -/* We use a separate counter for each CPU for both interrupt and non-interrupt - * context in order to keep the limit deterministic for a given packet. */ -struct percpu_loop_counters { - struct loop_counter counters[2]; -}; - -static DEFINE_PER_CPU(struct percpu_loop_counters, dp_loop_counters); - -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) @@ -112,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) @@ -127,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; @@ -151,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); @@ -177,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; @@ -269,8 +252,8 @@ static int create_dp(int dp_idx, const char __user *devnamep) /* Set up our datapath device. */ BUILD_BUG_ON(sizeof(internal_dev_port.devname) != sizeof(devname)); strcpy(internal_dev_port.devname, devname); - internal_dev_port.flags = ODP_PORT_INTERNAL; - err = new_dp_port(dp, &internal_dev_port, ODPP_LOCAL); + strcpy(internal_dev_port.type, "internal"); + err = new_vport(dp, &internal_dev_port, ODPP_LOCAL); if (err) { if (err == -EBUSY) err = -EEXIST; @@ -284,15 +267,15 @@ static int create_dp(int dp_idx, const char __user *devnamep) goto err_destroy_local_port; rcu_assign_pointer(dps[dp_idx], dp); + dp_sysfs_add_dp(dp); + mutex_unlock(&dp_mutex); rtnl_unlock(); - dp_sysfs_add_dp(dp); - return 0; err_destroy_local_port: - dp_detach_port(dp->ports[ODPP_LOCAL], 1); + dp_detach_port(dp->ports[ODPP_LOCAL]); err_destroy_table: tbl_destroy(dp->table, NULL); err_free_dp: @@ -308,18 +291,18 @@ 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) if (p->port_no != ODPP_LOCAL) - dp_detach_port(p, 1); + dp_detach_port(p); dp_sysfs_del_dp(dp); rcu_assign_pointer(dps[dp->dp_idx], NULL); - dp_detach_port(dp->ports[ODPP_LOCAL], 1); + dp_detach_port(dp->ports[ODPP_LOCAL]); tbl_destroy(dp->table, flow_free_tbl); @@ -351,66 +334,30 @@ 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; - - vport = vport_locate(odp_port->devname); - if (!vport) { - vport_lock(); - if (odp_port->flags & ODP_PORT_INTERNAL) - vport = vport_add(odp_port->devname, "internal", NULL); - else - vport = vport_add(odp_port->devname, "netdev", NULL); + parms.name = odp_port->devname; + parms.type = odp_port->type; + parms.config = odp_port->config; + parms.dp = dp; + parms.port_no = port_no; - vport_unlock(); + vport_lock(); + vport = vport_add(&parms); + vport_unlock(); - if (IS_ERR(vport)) - return PTR_ERR(vport); - } + 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; } @@ -426,6 +373,7 @@ static int attach_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.type[VPORT_TYPE_SIZE - 1] = '\0'; rtnl_lock(); dp = get_dp_locked(dp_idx); @@ -440,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; @@ -457,9 +405,8 @@ out: return err; } -int dp_detach_port(struct dp_port *p, int may_delete) +int dp_detach_port(struct vport *p) { - struct vport *vport = p->vport; int err; ASSERT_RTNL(); @@ -473,31 +420,17 @@ int dp_detach_port(struct dp_port *p, int may_delete) 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(); - - if (may_delete) { - const char *port_type = vport_get_type(vport); - - if (!strcmp(port_type, "netdev") || !strcmp(port_type, "internal")) { - vport_lock(); - vport_del(vport); - vport_unlock(); - } - } - - kobject_put(&p->kobj); + /* Then destroy it. */ + vport_lock(); + err = vport_del(p); + vport_unlock(); - 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; @@ -516,7 +449,7 @@ static int detach_port(int dp_idx, int port_no) if (!p) goto out_unlock_dp; - err = dp_detach_port(p, 1); + err = dp_detach_port(p); out_unlock_dp: mutex_unlock(&dp->mutex); @@ -526,16 +459,8 @@ out: return err; } -static void suppress_loop(struct datapath *dp, struct sw_flow_actions *actions) -{ - if (net_ratelimit()) - pr_warn("%s: flow looped %d times, dropping\n", - dp_name(dp), DP_MAX_LOOPS); - actions->n_actions = 0; -} - /* 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; @@ -544,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; @@ -581,28 +506,28 @@ void dp_process_received_packet(struct dp_port *p, struct sk_buff *skb) acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); /* Check whether we've looped too much. */ - loop = &get_cpu_var(dp_loop_counters).counters[!!in_interrupt()]; - if (unlikely(++loop->count > DP_MAX_LOOPS)) + loop = loop_get_counter(); + if (unlikely(++loop->count > MAX_LOOPS)) loop->looping = true; if (unlikely(loop->looping)) { - suppress_loop(dp, acts); + loop_suppress(dp, acts); goto out_loop; } /* Execute actions. */ execute_actions(dp, skb, &OVS_CB(skb)->flow->key, acts->actions, - acts->n_actions, GFP_ATOMIC); + acts->actions_len); stats_counter_off = offsetof(struct dp_stats_percpu, n_hit); /* Check whether sub-actions looped too much. */ if (unlikely(loop->looping)) - suppress_loop(dp, acts); + loop_suppress(dp, acts); out_loop: /* Decrement loop counter. */ if (!--loop->count) loop->looping = false; - put_cpu_var(dp_loop_counters); + loop_put_counter(); out: /* Update datapath statistics. */ @@ -616,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, @@ -790,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; @@ -898,56 +658,76 @@ static int flush_flows(struct datapath *dp) return 0; } -static int validate_actions(const struct sw_flow_actions *actions) -{ - unsigned int i; - - for (i = 0; i < actions->n_actions; i++) { - const union odp_action *a = &actions->actions[i]; - __be16 mask; - - switch (a->type) { - case ODPAT_CONTROLLER: - case ODPAT_STRIP_VLAN: - case ODPAT_SET_DL_SRC: - case ODPAT_SET_DL_DST: - case ODPAT_SET_NW_SRC: - case ODPAT_SET_NW_DST: - case ODPAT_SET_TP_SRC: - case ODPAT_SET_TP_DST: - case ODPAT_SET_TUNNEL: - case ODPAT_SET_PRIORITY: - case ODPAT_POP_PRIORITY: - case ODPAT_DROP_SPOOFED_ARP: - /* No validation needed. */ - break; +static int validate_actions(const struct nlattr *actions, u32 actions_len) +{ + const struct nlattr *a; + int rem; + + nla_for_each_attr(a, actions, actions_len, rem) { + static const u32 action_lens[ODPAT_MAX + 1] = { + [ODPAT_OUTPUT] = 4, + [ODPAT_CONTROLLER] = 4, + [ODPAT_SET_DL_TCI] = 2, + [ODPAT_STRIP_VLAN] = 0, + [ODPAT_SET_DL_SRC] = ETH_ALEN, + [ODPAT_SET_DL_DST] = ETH_ALEN, + [ODPAT_SET_NW_SRC] = 4, + [ODPAT_SET_NW_DST] = 4, + [ODPAT_SET_NW_TOS] = 1, + [ODPAT_SET_TP_SRC] = 2, + [ODPAT_SET_TP_DST] = 2, + [ODPAT_SET_TUNNEL] = 4, + [ODPAT_SET_PRIORITY] = 4, + [ODPAT_POP_PRIORITY] = 0, + [ODPAT_DROP_SPOOFED_ARP] = 0, + }; + int type = nla_type(a); + + if (type > ODPAT_MAX || nla_len(a) != action_lens[type]) + return -EINVAL; + + switch (type) { + case ODPAT_UNSPEC: + return -EINVAL; - case ODPAT_OUTPUT: - if (a->output.port >= DP_MAX_PORTS) + case ODPAT_CONTROLLER: + case ODPAT_STRIP_VLAN: + case ODPAT_SET_DL_SRC: + case ODPAT_SET_DL_DST: + case ODPAT_SET_NW_SRC: + case ODPAT_SET_NW_DST: + case ODPAT_SET_TP_SRC: + case ODPAT_SET_TP_DST: + case ODPAT_SET_TUNNEL: + case ODPAT_SET_PRIORITY: + case ODPAT_POP_PRIORITY: + case ODPAT_DROP_SPOOFED_ARP: + /* No validation needed. */ + break; + + case ODPAT_OUTPUT: + if (nla_get_u32(a) >= DP_MAX_PORTS) + return -EINVAL; + + case ODPAT_SET_DL_TCI: + if (nla_get_be16(a) & htons(VLAN_CFI_MASK)) return -EINVAL; - break; + break; - case ODPAT_SET_DL_TCI: - mask = a->dl_tci.mask; - if (mask != htons(VLAN_VID_MASK) && - mask != htons(VLAN_PCP_MASK) && - mask != htons(VLAN_VID_MASK | VLAN_PCP_MASK)) - return -EINVAL; - if (a->dl_tci.tci & ~mask) - return -EINVAL; - break; + case ODPAT_SET_NW_TOS: + if (nla_get_u8(a) & INET_ECN_MASK) + return -EINVAL; + break; - case ODPAT_SET_NW_TOS: - if (a->nw_tos.nw_tos & INET_ECN_MASK) - return -EINVAL; - break; + default: + return -EOPNOTSUPP; + } + } - default: - return -EOPNOTSUPP; - } - } + if (rem > 0) + return -EINVAL; - return 0; + return 0; } static struct sw_flow_actions *get_actions(const struct odp_flow *flow) @@ -955,16 +735,15 @@ static struct sw_flow_actions *get_actions(const struct odp_flow *flow) struct sw_flow_actions *actions; int error; - actions = flow_actions_alloc(flow->n_actions); + actions = flow_actions_alloc(flow->actions_len); error = PTR_ERR(actions); if (IS_ERR(actions)) goto error; error = -EFAULT; - if (copy_from_user(actions->actions, flow->actions, - flow->n_actions * sizeof(union odp_action))) + if (copy_from_user(actions->actions, flow->actions, flow->actions_len)) goto error_free_actions; - error = validate_actions(actions); + error = validate_actions(actions->actions, actions->actions_len); if (error) goto error_free_actions; @@ -976,24 +755,15 @@ error: return ERR_PTR(error); } -static struct timespec get_time_offset(void) -{ - struct timespec now_mono, now_jiffies; - - ktime_get_ts(&now_mono); - jiffies_to_timespec(jiffies, &now_jiffies); - return timespec_sub(now_mono, now_jiffies); -} - -static void get_stats(struct sw_flow *flow, struct odp_flow_stats *stats, - struct timespec time_offset) +static void get_stats(struct sw_flow *flow, struct odp_flow_stats *stats) { if (flow->used) { - struct timespec flow_ts, used; + struct timespec offset_ts, used, now_mono; - jiffies_to_timespec(flow->used, &flow_ts); - set_normalized_timespec(&used, flow_ts.tv_sec + time_offset.tv_sec, - flow_ts.tv_nsec + time_offset.tv_nsec); + ktime_get_ts(&now_mono); + jiffies_to_timespec(jiffies - flow->used, &offset_ts); + set_normalized_timespec(&used, now_mono.tv_sec - offset_ts.tv_sec, + now_mono.tv_nsec - offset_ts.tv_nsec); stats->used_sec = used.tv_sec; stats->used_nsec = used.tv_nsec; @@ -1097,9 +867,9 @@ static int do_put_flow(struct datapath *dp, struct odp_flow_put *uf, if (IS_ERR(new_acts)) goto error; old_acts = rcu_dereference(flow->sf_acts); - if (old_acts->n_actions != new_acts->n_actions || + if (old_acts->actions_len != new_acts->actions_len || memcmp(old_acts->actions, new_acts->actions, - sizeof(union odp_action) * old_acts->n_actions)) { + old_acts->actions_len)) { rcu_assign_pointer(flow->sf_acts, new_acts); flow_deferred_free_acts(old_acts); } else { @@ -1108,7 +878,7 @@ static int do_put_flow(struct datapath *dp, struct odp_flow_put *uf, /* Fetch stats, then clear them if necessary. */ spin_lock_bh(&flow->lock); - get_stats(flow, stats, get_time_offset()); + get_stats(flow, stats); if (uf->flags & ODPPF_ZERO_STATS) clear_stats(flow); spin_unlock_bh(&flow->lock); @@ -1146,50 +916,47 @@ static int put_flow(struct datapath *dp, struct odp_flow_put __user *ufp) } static int do_answer_query(struct sw_flow *flow, u32 query_flags, - struct timespec time_offset, struct odp_flow_stats __user *ustats, - union odp_action __user *actions, - u32 __user *n_actionsp) + struct nlattr __user *actions, + u32 __user *actions_lenp) { struct sw_flow_actions *sf_acts; struct odp_flow_stats stats; - u32 n_actions; + u32 actions_len; spin_lock_bh(&flow->lock); - get_stats(flow, &stats, time_offset); + get_stats(flow, &stats); if (query_flags & ODPFF_ZERO_TCP_FLAGS) flow->tcp_flags = 0; spin_unlock_bh(&flow->lock); if (copy_to_user(ustats, &stats, sizeof(struct odp_flow_stats)) || - get_user(n_actions, n_actionsp)) + get_user(actions_len, actions_lenp)) return -EFAULT; - if (!n_actions) + if (!actions_len) return 0; sf_acts = rcu_dereference(flow->sf_acts); - if (put_user(sf_acts->n_actions, n_actionsp) || + if (put_user(sf_acts->actions_len, actions_lenp) || (actions && copy_to_user(actions, sf_acts->actions, - sizeof(union odp_action) * - min(sf_acts->n_actions, n_actions)))) + min(sf_acts->actions_len, actions_len)))) return -EFAULT; return 0; } static int answer_query(struct sw_flow *flow, u32 query_flags, - struct timespec time_offset, struct odp_flow __user *ufp) { - union odp_action *actions; + struct nlattr *actions; if (get_user(actions, &ufp->actions)) return -EFAULT; - return do_answer_query(flow, query_flags, time_offset, - &ufp->stats, actions, &ufp->n_actions); + return do_answer_query(flow, query_flags, + &ufp->stats, actions, &ufp->actions_len); } static struct sw_flow *do_del_flow(struct datapath *dp, struct odp_flow_key *key) @@ -1226,7 +993,7 @@ static int del_flow(struct datapath *dp, struct odp_flow __user *ufp) if (IS_ERR(flow)) return PTR_ERR(flow); - error = answer_query(flow, 0, get_time_offset(), ufp); + error = answer_query(flow, 0, ufp); flow_deferred_free(flow); return error; } @@ -1234,11 +1001,8 @@ static int del_flow(struct datapath *dp, struct odp_flow __user *ufp) static int do_query_flows(struct datapath *dp, const struct odp_flowvec *flowvec) { struct tbl *table = rcu_dereference(dp->table); - struct timespec time_offset; u32 i; - time_offset = get_time_offset(); - for (i = 0; i < flowvec->n_flows; i++) { struct odp_flow __user *ufp = &flowvec->flows[i]; struct odp_flow uf; @@ -1252,7 +1016,7 @@ static int do_query_flows(struct datapath *dp, const struct odp_flowvec *flowvec if (!flow_node) error = put_user(ENOENT, &ufp->stats.error); else - error = answer_query(flow_cast(flow_node), uf.flags, time_offset, ufp); + error = answer_query(flow_cast(flow_node), uf.flags, ufp); if (error) return -EFAULT; } @@ -1263,7 +1027,6 @@ struct list_flows_cbdata { struct odp_flow __user *uflows; u32 n_flows; u32 listed_flows; - struct timespec time_offset; }; static int list_flow(struct tbl_node *node, void *cbdata_) @@ -1275,7 +1038,7 @@ static int list_flow(struct tbl_node *node, void *cbdata_) if (copy_to_user(&ufp->key, &flow->key, sizeof flow->key)) return -EFAULT; - error = answer_query(flow, 0, cbdata->time_offset, ufp); + error = answer_query(flow, 0, ufp); if (error) return error; @@ -1295,7 +1058,6 @@ static int do_list_flows(struct datapath *dp, const struct odp_flowvec *flowvec) cbdata.uflows = flowvec->flows; cbdata.n_flows = flowvec->n_flows; cbdata.listed_flows = 0; - cbdata.time_offset = get_time_offset(); error = tbl_foreach(rcu_dereference(dp->table), list_flow, &cbdata); return error ? error : cbdata.listed_flows; @@ -1335,18 +1097,17 @@ static int do_execute(struct datapath *dp, const struct odp_execute *execute) if (execute->length < ETH_HLEN || execute->length > 65535) goto error; - actions = flow_actions_alloc(execute->n_actions); + actions = flow_actions_alloc(execute->actions_len); if (IS_ERR(actions)) { err = PTR_ERR(actions); goto error; } err = -EFAULT; - if (copy_from_user(actions->actions, execute->actions, - execute->n_actions * sizeof *execute->actions)) + if (copy_from_user(actions->actions, execute->actions, execute->actions_len)) goto error_free_actions; - err = validate_actions(actions); + err = validate_actions(actions->actions, execute->actions_len); if (err) goto error_free_actions; @@ -1376,8 +1137,7 @@ static int do_execute(struct datapath *dp, const struct odp_execute *execute) goto error_free_skb; rcu_read_lock(); - err = execute_actions(dp, skb, &key, actions->actions, - actions->n_actions, GFP_KERNEL); + err = execute_actions(dp, skb, &key, actions->actions, actions->actions_len); rcu_read_unlock(); kfree(actions); @@ -1438,7 +1198,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(); @@ -1448,10 +1208,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; } @@ -1463,7 +1223,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(); @@ -1471,23 +1231,23 @@ 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.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; - op.flags = is_internal_vport(p->vport) ? ODP_PORT_INTERNAL : 0; return copy_to_user(uop, &op, sizeof op) ? -EFAULT : 0; } @@ -1501,7 +1261,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'; @@ -1514,14 +1273,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(); @@ -1544,7 +1301,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])) @@ -1600,26 +1357,18 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd, err = destroy_dp(dp_idx); goto exit; - case ODP_PORT_ATTACH: + case ODP_VPORT_ATTACH: err = attach_port(dp_idx, (struct odp_port __user *)argp); goto exit; - case ODP_PORT_DETACH: + case ODP_VPORT_DETACH: err = get_user(port_no, (int __user *)argp); if (!err) err = detach_port(dp_idx, port_no); goto exit; - case ODP_VPORT_ADD: - err = vport_user_add((struct odp_vport_add __user *)argp); - goto exit; - case ODP_VPORT_MOD: - err = vport_user_mod((struct odp_vport_mod __user *)argp); - goto exit; - - case ODP_VPORT_DEL: - err = vport_user_del((char __user *)argp); + err = vport_user_mod((struct odp_port __user *)argp); goto exit; case ODP_VPORT_STATS_GET: @@ -1697,11 +1446,11 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd, dp->sflow_probability = sflow_probability; break; - case ODP_PORT_QUERY: + case ODP_VPORT_QUERY: err = query_port(dp, (struct odp_port __user *)argp); break; - case ODP_PORT_LIST: + case ODP_VPORT_LIST: err = list_ports(dp, (struct odp_portvec __user *)argp); break; @@ -1772,7 +1521,7 @@ static int compat_get_flow(struct odp_flow *flow, const struct compat_odp_flow _ __copy_from_user(&flow->stats, &compat->stats, sizeof(struct odp_flow_stats)) || __copy_from_user(&flow->key, &compat->key, sizeof(struct odp_flow_key)) || __get_user(actions, &compat->actions) || - __get_user(flow->n_actions, &compat->n_actions) || + __get_user(flow->actions_len, &compat->actions_len) || __get_user(flow->flags, &compat->flags)) return -EFAULT; @@ -1802,7 +1551,6 @@ static int compat_put_flow(struct datapath *dp, struct compat_odp_flow_put __use } static int compat_answer_query(struct sw_flow *flow, u32 query_flags, - struct timespec time_offset, struct compat_odp_flow __user *ufp) { compat_uptr_t actions; @@ -1810,8 +1558,8 @@ static int compat_answer_query(struct sw_flow *flow, u32 query_flags, if (get_user(actions, &ufp->actions)) return -EFAULT; - return do_answer_query(flow, query_flags, time_offset, &ufp->stats, - compat_ptr(actions), &ufp->n_actions); + return do_answer_query(flow, query_flags, &ufp->stats, + compat_ptr(actions), &ufp->actions_len); } static int compat_del_flow(struct datapath *dp, struct compat_odp_flow __user *ufp) @@ -1827,7 +1575,7 @@ static int compat_del_flow(struct datapath *dp, struct compat_odp_flow __user *u if (IS_ERR(flow)) return PTR_ERR(flow); - error = compat_answer_query(flow, 0, get_time_offset(), ufp); + error = compat_answer_query(flow, 0, ufp); flow_deferred_free(flow); return error; } @@ -1835,11 +1583,8 @@ static int compat_del_flow(struct datapath *dp, struct compat_odp_flow __user *u static int compat_query_flows(struct datapath *dp, struct compat_odp_flow *flows, u32 n_flows) { struct tbl *table = rcu_dereference(dp->table); - struct timespec time_offset; u32 i; - time_offset = get_time_offset(); - for (i = 0; i < n_flows; i++) { struct compat_odp_flow __user *ufp = &flows[i]; struct odp_flow uf; @@ -1848,13 +1593,12 @@ static int compat_query_flows(struct datapath *dp, struct compat_odp_flow *flows if (compat_get_flow(&uf, ufp)) return -EFAULT; - memset(uf.key.reserved, 0, sizeof uf.key.reserved); flow_node = tbl_lookup(table, &uf.key, flow_hash(&uf.key), flow_cmp); if (!flow_node) error = put_user(ENOENT, &ufp->stats.error); else - error = compat_answer_query(flow_cast(flow_node), uf.flags, time_offset, ufp); + error = compat_answer_query(flow_cast(flow_node), uf.flags, ufp); if (error) return -EFAULT; } @@ -1865,7 +1609,6 @@ struct compat_list_flows_cbdata { struct compat_odp_flow __user *uflows; u32 n_flows; u32 listed_flows; - struct timespec time_offset; }; static int compat_list_flow(struct tbl_node *node, void *cbdata_) @@ -1877,7 +1620,7 @@ static int compat_list_flow(struct tbl_node *node, void *cbdata_) if (copy_to_user(&ufp->key, &flow->key, sizeof flow->key)) return -EFAULT; - error = compat_answer_query(flow, 0, cbdata->time_offset, ufp); + error = compat_answer_query(flow, 0, ufp); if (error) return error; @@ -1897,7 +1640,6 @@ static int compat_list_flows(struct datapath *dp, struct compat_odp_flow *flows, cbdata.uflows = flows; cbdata.n_flows = n_flows; cbdata.listed_flows = 0; - cbdata.time_offset = get_time_offset(); error = tbl_foreach(rcu_dereference(dp->table), compat_list_flow, &cbdata); return error ? error : cbdata.listed_flows; @@ -1940,7 +1682,7 @@ static int compat_execute(struct datapath *dp, const struct compat_odp_execute _ if (!access_ok(VERIFY_READ, uexecute, sizeof(struct compat_odp_execute)) || __get_user(actions, &uexecute->actions) || - __get_user(execute.n_actions, &uexecute->n_actions) || + __get_user(execute.actions_len, &uexecute->actions_len) || __get_user(data, &uexecute->data) || __get_user(execute.length, &uexecute->length)) return -EFAULT; @@ -1964,9 +1706,9 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned return openvswitch_ioctl(f, cmd, argp); case ODP_DP_CREATE: - case ODP_PORT_ATTACH: - case ODP_PORT_DETACH: - case ODP_VPORT_DEL: + case ODP_VPORT_ATTACH: + case ODP_VPORT_DETACH: + case ODP_VPORT_MOD: case ODP_VPORT_MTU_SET: case ODP_VPORT_MTU_GET: case ODP_VPORT_ETHER_SET: @@ -1980,15 +1722,9 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned case ODP_GET_LISTEN_MASK: case ODP_SET_SFLOW_PROBABILITY: case ODP_GET_SFLOW_PROBABILITY: - case ODP_PORT_QUERY: + case ODP_VPORT_QUERY: /* Ioctls that just need their pointer argument extended. */ return openvswitch_ioctl(f, cmd, (unsigned long)compat_ptr(argp)); - - case ODP_VPORT_ADD32: - return compat_vport_user_add(compat_ptr(argp)); - - case ODP_VPORT_MOD32: - return compat_vport_user_mod(compat_ptr(argp)); } dp = get_dp_locked(dp_idx); @@ -1997,7 +1733,7 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned goto exit; switch (cmd) { - case ODP_PORT_LIST32: + case ODP_VPORT_LIST32: err = compat_list_ports(dp, compat_ptr(argp)); break; @@ -2125,13 +1861,12 @@ fault: return -EFAULT; } -ssize_t openvswitch_read(struct file *f, char __user *buf, size_t nbytes, - loff_t *ppos) +static ssize_t openvswitch_read(struct file *f, char __user *buf, + size_t nbytes, loff_t *ppos) { - /* XXX is there sufficient synchronization here? */ int listeners = get_listen_mask(f); int dp_idx = iminor(f->f_dentry->d_inode); - struct datapath *dp = get_dp(dp_idx); + struct datapath *dp = get_dp_locked(dp_idx); struct sk_buff *skb; size_t copy_bytes, tot_copy_bytes; int retval; @@ -2168,21 +1903,17 @@ ssize_t openvswitch_read(struct file *f, char __user *buf, size_t nbytes, } } success: + mutex_unlock(&dp->mutex); + copy_bytes = tot_copy_bytes = min_t(size_t, skb->len, nbytes); retval = 0; 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); @@ -2211,16 +1942,17 @@ success: retval = tot_copy_bytes; kfree_skb(skb); + return retval; error: + mutex_unlock(&dp->mutex); return retval; } static unsigned int openvswitch_poll(struct file *file, poll_table *wait) { - /* XXX is there sufficient synchronization here? */ int dp_idx = iminor(file->f_dentry->d_inode); - struct datapath *dp = get_dp(dp_idx); + struct datapath *dp = get_dp_locked(dp_idx); unsigned int mask; if (dp) { @@ -2228,21 +1960,20 @@ static unsigned int openvswitch_poll(struct file *file, poll_table *wait) poll_wait(file, &dp->waitqueue, wait); if (dp_has_packet_of_interest(dp, get_listen_mask(file))) mask |= POLLIN | POLLRDNORM; + mutex_unlock(&dp->mutex); } else { mask = POLLIN | POLLRDNORM | POLLHUP; } return mask; } -struct file_operations openvswitch_fops = { - /* XXX .aio_read = openvswitch_aio_read, */ +static struct file_operations openvswitch_fops = { .read = openvswitch_read, .poll = openvswitch_poll, .unlocked_ioctl = openvswitch_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = openvswitch_compat_ioctl, #endif - /* XXX .fasync = openvswitch_fasync, */ }; static int major;