2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 06:15:47 +00:00

dpif: Allow execute to modify the packet.

Allowing the packet to be modified by execution allows less data
copying for userspace action execution.  Some users of the
dpif_execute already expect that the packet may be modified.  This
patch makes this behavior uniform and makes the userspace datapath and
the execution helpers modify the packet as it is being executed.
Userspace action now steals the packet if given permission, as the
packet is normally not needed after it.  The only exception is the
sample action, and this is accounted for my keeping track of any
actions that could be following the userspace action.

The packet in dpif_upcall is changed from a pointer to a struct,
allowing the packet to be honest about it's headroom.  After this
change the packet can safely be pushed on over the precarious 4 byte
limit earlier allowed by the netlink data preceding the packet.

Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Jarno Rajahalme
2013-12-16 08:14:52 -08:00
parent 881d47a9fa
commit da546e0764
11 changed files with 135 additions and 112 deletions

View File

@@ -166,7 +166,7 @@ static int do_add_port(struct dp_netdev *, const char *devname,
static int do_del_port(struct dp_netdev *, odp_port_t port_no);
static int dpif_netdev_open(const struct dpif_class *, const char *name,
bool create, struct dpif **);
static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *,
static int dp_netdev_output_userspace(struct dp_netdev *, struct ofpbuf *,
int queue_no, const struct flow *,
const struct nlattr *userdata);
static void dp_netdev_execute_actions(struct dp_netdev *, const struct flow *,
@@ -344,6 +344,7 @@ dp_netdev_purge_queues(struct dp_netdev *dp)
while (q->tail != q->head) {
struct dp_netdev_upcall *u = &q->upcalls[q->tail++ & QUEUE_MASK];
ofpbuf_uninit(&u->upcall.packet);
ofpbuf_uninit(&u->buf);
}
}
@@ -1154,20 +1155,15 @@ dpif_netdev_execute(struct dpif *dpif, const struct dpif_execute *execute)
/* Get packet metadata. */
error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len, &md);
if (!error) {
struct ofpbuf *copy;
struct flow key;
/* Make a deep copy of 'packet', because we might modify its data. */
copy = ofpbuf_clone_with_headroom(execute->packet, DP_NETDEV_HEADROOM);
/* Extract flow key. */
flow_extract(copy, md.skb_priority, md.pkt_mark, &md.tunnel,
flow_extract(execute->packet, md.skb_priority, md.pkt_mark, &md.tunnel,
&md.in_port, &key);
ovs_mutex_lock(&dp_netdev_mutex);
dp_netdev_execute_actions(dp, &key, copy,
dp_netdev_execute_actions(dp, &key, execute->packet,
execute->actions, execute->actions_len);
ovs_mutex_unlock(&dp_netdev_mutex);
ofpbuf_delete(copy);
}
return error;
}
@@ -1214,7 +1210,6 @@ dpif_netdev_recv(struct dpif *dpif, struct dpif_upcall *upcall,
struct dp_netdev_upcall *u = &q->upcalls[q->tail++ & QUEUE_MASK];
*upcall = u->upcall;
upcall->packet = buf;
ofpbuf_uninit(buf);
*buf = u->buf;
@@ -1296,18 +1291,20 @@ dpif_netdev_run(struct dpif *dpif)
struct dp_netdev_port *port;
struct dp_netdev *dp;
struct ofpbuf packet;
size_t buf_size;
ovs_mutex_lock(&dp_netdev_mutex);
dp = get_dp_netdev(dpif);
ofpbuf_init(&packet,
DP_NETDEV_HEADROOM + VLAN_ETH_HEADER_LEN + dp->max_mtu);
ofpbuf_init(&packet, 0);
buf_size = DP_NETDEV_HEADROOM + VLAN_ETH_HEADER_LEN + dp->max_mtu;
LIST_FOR_EACH (port, node, &dp->port_list) {
int error;
/* Reset packet contents. */
/* Reset packet contents. Packet data may have been stolen. */
ofpbuf_clear(&packet);
ofpbuf_reserve(&packet, DP_NETDEV_HEADROOM);
ofpbuf_reserve_with_tailroom(&packet, DP_NETDEV_HEADROOM, buf_size);
error = port->rx ? netdev_rx_recv(port->rx, &packet) : EOPNOTSUPP;
if (!error) {
@@ -1351,7 +1348,7 @@ dpif_netdev_wait(struct dpif *dpif)
}
static int
dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
int queue_no, const struct flow *flow,
const struct nlattr *userdata)
{
@@ -1365,7 +1362,7 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
upcall->type = queue_no;
/* Allocate buffer big enough for everything. */
buf_size = ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size;
buf_size = ODPUTIL_FLOW_KEY_BYTES;
if (userdata) {
buf_size += NLA_ALIGN(userdata->nla_len);
}
@@ -1382,15 +1379,10 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
NLA_ALIGN(userdata->nla_len));
}
/* Put packet.
*
* We adjust 'data' and 'size' in 'buf' so that only the packet itself
* is visible in 'upcall->packet'. The ODP flow and (if present)
* userdata become part of the headroom. */
ofpbuf_put_zeros(buf, 2);
buf->data = ofpbuf_put(buf, packet->data, packet->size);
buf->size = packet->size;
upcall->packet = buf;
/* Steal packet data. */
ovs_assert(packet->source == OFPBUF_MALLOC);
upcall->packet = *packet;
ofpbuf_use(packet, NULL, 0);
seq_change(dp->queue_seq);
@@ -1421,14 +1413,22 @@ dp_netdev_action_output(void *aux_, struct ofpbuf *packet,
static void
dp_netdev_action_userspace(void *aux_, struct ofpbuf *packet,
const struct flow *flow OVS_UNUSED,
const struct nlattr *a)
const struct nlattr *a, bool may_steal)
{
struct dp_netdev_execute_aux *aux = aux_;
const struct nlattr *userdata;
userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
/* Make a copy if we are not allowed to steal the packet's data. */
if (!may_steal) {
packet = ofpbuf_clone_with_headroom(packet, DP_NETDEV_HEADROOM);
}
dp_netdev_output_userspace(aux->dp, packet, DPIF_UC_ACTION, aux->key,
userdata);
if (!may_steal) {
ofpbuf_uninit(packet);
}
}
static void