X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=datapath%2Fdatapath.c;h=bed48039b63a2e0d9a8b1b0870b53f96a1bbe7e4;hb=b279fccf5bd8c5addfb8e73b04103405b6a8237e;hp=390acc8a4e5f8a029fbf207b58c6eb45593aab3a;hpb=3976f6d57b1134c5c3ed054c9da4aa6786fbf5bf;p=openvswitch diff --git a/datapath/datapath.c b/datapath/datapath.c index 390acc8a..bed48039 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -48,13 +47,13 @@ #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); @@ -70,23 +69,6 @@ EXPORT_SYMBOL(dp_ioctl_hook); 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); /* Must be called with rcu_read_lock or dp_mutex. */ @@ -326,8 +308,6 @@ static void do_destroy_dp(struct datapath *dp) for (i = 0; i < DP_N_QUEUES; i++) skb_queue_purge(&dp->queues[i]); - for (i = 0; i < DP_MAX_GROUPS; i++) - kfree(dp->groups[i]); free_percpu(dp->stats_percpu); kobject_put(&dp->ifobj); module_put(THIS_MODULE); @@ -529,14 +509,6 @@ 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) { @@ -552,15 +524,16 @@ void dp_process_received_packet(struct dp_port *p, struct sk_buff *skb) if (!OVS_CB(skb)->flow) { struct odp_flow_key key; struct tbl_node *flow_node; + bool is_frag; /* Extract flow from 'skb' into 'key'. */ - error = flow_extract(skb, p ? p->port_no : ODPP_NONE, &key); + error = flow_extract(skb, p ? p->port_no : ODPP_NONE, &key, &is_frag); if (unlikely(error)) { kfree_skb(skb); return; } - if (OVS_CB(skb)->is_frag && dp->drop_frags) { + if (is_frag && dp->drop_frags) { kfree_skb(skb); stats_counter_off = offsetof(struct dp_stats_percpu, n_frags); goto out; @@ -583,28 +556,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->n_actions); 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. */ @@ -906,25 +879,30 @@ static int validate_actions(const struct sw_flow_actions *actions) for (i = 0; i < actions->n_actions; i++) { const union odp_action *a = &actions->actions[i]; - switch (a->type) { - case ODPAT_OUTPUT: - if (a->output.port >= DP_MAX_PORTS) - return -EINVAL; - break; - case ODPAT_OUTPUT_GROUP: - if (a->output_group.group >= DP_MAX_GROUPS) - return -EINVAL; + 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; - case ODPAT_SET_VLAN_VID: - if (a->vlan_vid.vlan_vid & htons(~VLAN_VID_MASK)) + case ODPAT_OUTPUT: + if (a->output.port >= DP_MAX_PORTS) return -EINVAL; break; - case ODPAT_SET_VLAN_PCP: - if (a->vlan_pcp.vlan_pcp - & ~(VLAN_PCP_MASK >> VLAN_PCP_SHIFT)) + case ODPAT_SET_DL_TCI: + if (a->dl_tci.tci & htons(VLAN_CFI_MASK)) return -EINVAL; break; @@ -934,9 +912,7 @@ static int validate_actions(const struct sw_flow_actions *actions) break; default: - if (a->type >= ODPAT_N_ACTIONS) - return -EOPNOTSUPP; - break; + return -EOPNOTSUPP; } } @@ -969,24 +945,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; @@ -1033,8 +1000,6 @@ static int do_put_flow(struct datapath *dp, struct odp_flow_put *uf, struct tbl *table; int error; - memset(uf->flow.key.reserved, 0, sizeof uf->flow.key.reserved); - table = rcu_dereference(dp->table); flow_node = tbl_lookup(table, &uf->flow.key, flow_hash(&uf->flow.key), flow_cmp); if (!flow_node) { @@ -1103,7 +1068,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); @@ -1141,7 +1106,6 @@ 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) @@ -1151,7 +1115,7 @@ static int do_answer_query(struct sw_flow *flow, u32 query_flags, u32 n_actions; 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; @@ -1175,7 +1139,6 @@ static int do_answer_query(struct sw_flow *flow, u32 query_flags, } static int answer_query(struct sw_flow *flow, u32 query_flags, - struct timespec time_offset, struct odp_flow __user *ufp) { union odp_action *actions; @@ -1183,7 +1146,7 @@ static int 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, + return do_answer_query(flow, query_flags, &ufp->stats, actions, &ufp->n_actions); } @@ -1193,7 +1156,6 @@ static struct sw_flow *do_del_flow(struct datapath *dp, struct odp_flow_key *key struct tbl_node *flow_node; int error; - memset(key->reserved, 0, sizeof key->reserved); flow_node = tbl_lookup(table, key, flow_hash(key), flow_cmp); if (!flow_node) return ERR_PTR(-ENOENT); @@ -1222,7 +1184,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; } @@ -1230,11 +1192,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; @@ -1243,13 +1202,12 @@ static int do_query_flows(struct datapath *dp, const struct odp_flowvec *flowvec if (copy_from_user(&uf, ufp, sizeof uf)) 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 = 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; } @@ -1260,7 +1218,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_) @@ -1272,7 +1229,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; @@ -1292,7 +1249,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; @@ -1325,6 +1281,7 @@ static int do_execute(struct datapath *dp, const struct odp_execute *execute) struct sk_buff *skb; struct sw_flow_actions *actions; struct ethhdr *eth; + bool is_frag; int err; err = -EINVAL; @@ -1351,11 +1308,6 @@ static int do_execute(struct datapath *dp, const struct odp_execute *execute) if (!skb) goto error_free_actions; - if (execute->in_port < DP_MAX_PORTS) - OVS_CB(skb)->dp_port = dp->ports[execute->in_port]; - else - OVS_CB(skb)->dp_port = NULL; - err = -EFAULT; if (copy_from_user(skb_put(skb, execute->length), execute->data, execute->length)) @@ -1372,13 +1324,12 @@ static int do_execute(struct datapath *dp, const struct odp_execute *execute) else skb->protocol = htons(ETH_P_802_2); - err = flow_extract(skb, execute->in_port, &key); + err = flow_extract(skb, -1, &key, &is_frag); if (err) 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->n_actions); rcu_read_unlock(); kfree(actions); @@ -1413,7 +1364,6 @@ static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp) stats.max_capacity = TBL_MAX_BUCKETS; stats.n_ports = dp->n_ports; stats.max_ports = DP_MAX_PORTS; - stats.max_groups = DP_MAX_GROUPS; stats.n_frags = stats.n_hit = stats.n_missed = stats.n_lost = 0; for_each_possible_cpu(i) { const struct dp_stats_percpu *percpu_stats; @@ -1573,87 +1523,6 @@ static int list_ports(struct datapath *dp, struct odp_portvec __user *upv) return put_user(retval, &upv->n_ports); } -/* RCU callback for freeing a dp_port_group */ -static void free_port_group(struct rcu_head *rcu) -{ - struct dp_port_group *g = container_of(rcu, struct dp_port_group, rcu); - kfree(g); -} - -static int do_set_port_group(struct datapath *dp, u16 __user *ports, - int n_ports, int group) -{ - struct dp_port_group *new_group, *old_group; - int error; - - error = -EINVAL; - if (n_ports > DP_MAX_PORTS || group >= DP_MAX_GROUPS) - goto error; - - error = -ENOMEM; - new_group = kmalloc(sizeof *new_group + sizeof(u16) * n_ports, GFP_KERNEL); - if (!new_group) - goto error; - - new_group->n_ports = n_ports; - error = -EFAULT; - if (copy_from_user(new_group->ports, ports, sizeof(u16) * n_ports)) - goto error_free; - - old_group = rcu_dereference(dp->groups[group]); - rcu_assign_pointer(dp->groups[group], new_group); - if (old_group) - call_rcu(&old_group->rcu, free_port_group); - return 0; - -error_free: - kfree(new_group); -error: - return error; -} - -static int set_port_group(struct datapath *dp, - const struct odp_port_group __user *upg) -{ - struct odp_port_group pg; - - if (copy_from_user(&pg, upg, sizeof pg)) - return -EFAULT; - - return do_set_port_group(dp, pg.ports, pg.n_ports, pg.group); -} - -static int do_get_port_group(struct datapath *dp, - u16 __user *ports, int n_ports, int group, - u16 __user *n_portsp) -{ - struct dp_port_group *g; - u16 n_copy; - - if (group >= DP_MAX_GROUPS) - return -EINVAL; - - g = dp->groups[group]; - n_copy = g ? min_t(int, g->n_ports, n_ports) : 0; - if (n_copy && copy_to_user(ports, g->ports, n_copy * sizeof(u16))) - return -EFAULT; - - if (put_user(g ? g->n_ports : 0, n_portsp)) - return -EFAULT; - - return 0; -} - -static int get_port_group(struct datapath *dp, struct odp_port_group __user *upg) -{ - struct odp_port_group pg; - - if (copy_from_user(&pg, upg, sizeof pg)) - return -EFAULT; - - return do_get_port_group(dp, pg.ports, pg.n_ports, pg.group, &upg->n_ports); -} - static int get_listen_mask(const struct file *f) { return (long)f->private_data; @@ -1788,14 +1657,6 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd, err = list_ports(dp, (struct odp_portvec __user *)argp); break; - case ODP_PORT_GROUP_SET: - err = set_port_group(dp, (struct odp_port_group __user *)argp); - break; - - case ODP_PORT_GROUP_GET: - err = get_port_group(dp, (struct odp_port_group __user *)argp); - break; - case ODP_FLOW_FLUSH: err = flush_flows(dp); break; @@ -1855,27 +1716,6 @@ static int compat_list_ports(struct datapath *dp, struct compat_odp_portvec __us return put_user(retval, &upv->n_ports); } -static int compat_set_port_group(struct datapath *dp, const struct compat_odp_port_group __user *upg) -{ - struct compat_odp_port_group pg; - - if (copy_from_user(&pg, upg, sizeof pg)) - return -EFAULT; - - return do_set_port_group(dp, compat_ptr(pg.ports), pg.n_ports, pg.group); -} - -static int compat_get_port_group(struct datapath *dp, struct compat_odp_port_group __user *upg) -{ - struct compat_odp_port_group pg; - - if (copy_from_user(&pg, upg, sizeof pg)) - return -EFAULT; - - return do_get_port_group(dp, compat_ptr(pg.ports), pg.n_ports, - pg.group, &upg->n_ports); -} - static int compat_get_flow(struct odp_flow *flow, const struct compat_odp_flow __user *compat) { compat_uptr_t actions; @@ -1914,7 +1754,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; @@ -1922,7 +1761,7 @@ 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, + return do_answer_query(flow, query_flags, &ufp->stats, compat_ptr(actions), &ufp->n_actions); } @@ -1939,7 +1778,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; } @@ -1947,11 +1786,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; @@ -1960,13 +1796,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; } @@ -1977,7 +1812,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_) @@ -1989,7 +1823,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; @@ -2009,7 +1843,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; @@ -2051,7 +1884,6 @@ static int compat_execute(struct datapath *dp, const struct compat_odp_execute _ compat_uptr_t data; if (!access_ok(VERIFY_READ, uexecute, sizeof(struct compat_odp_execute)) || - __get_user(execute.in_port, &uexecute->in_port) || __get_user(actions, &uexecute->actions) || __get_user(execute.n_actions, &uexecute->n_actions) || __get_user(data, &uexecute->data) || @@ -2114,14 +1946,6 @@ static long openvswitch_compat_ioctl(struct file *f, unsigned int cmd, unsigned err = compat_list_ports(dp, compat_ptr(argp)); break; - case ODP_PORT_GROUP_SET32: - err = compat_set_port_group(dp, compat_ptr(argp)); - break; - - case ODP_PORT_GROUP_GET32: - err = compat_get_port_group(dp, compat_ptr(argp)); - break; - case ODP_FLOW_PUT32: err = compat_put_flow(dp, compat_ptr(argp)); break;