+static int odp_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
+{
+ struct odp_header *odp_header = info->userhdr;
+ struct nlattr **a = info->attrs;
+ struct sw_flow_actions *acts;
+ struct sk_buff *packet;
+ struct sw_flow *flow;
+ struct datapath *dp;
+ struct ethhdr *eth;
+ bool is_frag;
+ int len;
+ int err;
+ int key_len;
+
+ err = -EINVAL;
+ if (!a[ODP_PACKET_ATTR_PACKET] || !a[ODP_PACKET_ATTR_KEY] ||
+ !a[ODP_PACKET_ATTR_ACTIONS] ||
+ nla_len(a[ODP_PACKET_ATTR_PACKET]) < ETH_HLEN)
+ goto err;
+
+ err = validate_actions(a[ODP_PACKET_ATTR_ACTIONS]);
+ if (err)
+ goto err;
+
+ len = nla_len(a[ODP_PACKET_ATTR_PACKET]);
+ packet = __dev_alloc_skb(NET_IP_ALIGN + len, GFP_KERNEL);
+ err = -ENOMEM;
+ if (!packet)
+ goto err;
+ skb_reserve(packet, NET_IP_ALIGN);
+
+ memcpy(__skb_put(packet, len), nla_data(a[ODP_PACKET_ATTR_PACKET]), len);
+
+ skb_reset_mac_header(packet);
+ eth = eth_hdr(packet);
+
+ /* Normally, setting the skb 'protocol' field would be handled by a
+ * call to eth_type_trans(), but it assumes there's a sending
+ * device, which we may not have. */
+ if (ntohs(eth->h_proto) >= 1536)
+ packet->protocol = eth->h_proto;
+ else
+ packet->protocol = htons(ETH_P_802_2);
+
+ /* Build an sw_flow for sending this packet. */
+ flow = flow_alloc();
+ err = PTR_ERR(flow);
+ if (IS_ERR(flow))
+ goto err_kfree_skb;
+
+ err = flow_extract(packet, -1, &flow->key, &key_len, &is_frag);
+ if (err)
+ goto err_flow_put;
+ flow->tbl_node.hash = flow_hash(&flow->key, key_len);
+
+ err = flow_metadata_from_nlattrs(&flow->key.eth.in_port,
+ &flow->key.eth.tun_id,
+ a[ODP_PACKET_ATTR_KEY]);
+ if (err)
+ goto err_flow_put;
+
+ acts = flow_actions_alloc(a[ODP_PACKET_ATTR_ACTIONS]);
+ err = PTR_ERR(acts);
+ if (IS_ERR(acts))
+ goto err_flow_put;
+ rcu_assign_pointer(flow->sf_acts, acts);
+
+ OVS_CB(packet)->flow = flow;
+
+ rcu_read_lock();
+ dp = get_dp(odp_header->dp_ifindex);
+ err = -ENODEV;
+ if (!dp)
+ goto err_unlock;
+ err = execute_actions(dp, packet);
+ rcu_read_unlock();
+
+ flow_put(flow);
+ return err;
+
+err_unlock:
+ rcu_read_unlock();
+err_flow_put:
+ flow_put(flow);
+err_kfree_skb:
+ kfree_skb(packet);
+err:
+ return err;
+}
+
+static const struct nla_policy packet_policy[ODP_PACKET_ATTR_MAX + 1] = {
+ [ODP_PACKET_ATTR_PACKET] = { .type = NLA_UNSPEC },
+ [ODP_PACKET_ATTR_KEY] = { .type = NLA_NESTED },
+ [ODP_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED },
+};
+
+static struct genl_ops dp_packet_genl_ops[] = {
+ { .cmd = ODP_PACKET_CMD_EXECUTE,
+ .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
+ .policy = packet_policy,
+ .doit = odp_packet_cmd_execute
+ }
+};
+
+static void get_dp_stats(struct datapath *dp, struct odp_stats *stats)
+{
+ int i;
+
+ 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;
+ struct dp_stats_percpu local_stats;
+ unsigned seqcount;
+
+ percpu_stats = per_cpu_ptr(dp->stats_percpu, i);
+
+ do {
+ seqcount = read_seqcount_begin(&percpu_stats->seqlock);
+ local_stats = *percpu_stats;
+ } while (read_seqcount_retry(&percpu_stats->seqlock, seqcount));
+
+ stats->n_frags += local_stats.n_frags;
+ stats->n_hit += local_stats.n_hit;
+ stats->n_missed += local_stats.n_missed;
+ stats->n_lost += local_stats.n_lost;
+ }