/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <config.h>
#include "dpif.h"
-#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include "hmap.h"
#include "list.h"
#include "netdev.h"
+#include "netdev-vport.h"
#include "netlink.h"
#include "odp-util.h"
#include "ofp-print.h"
int port_no; /* Index into dp_netdev's 'ports'. */
struct list node; /* Element in dp_netdev's 'port_list'. */
struct netdev *netdev;
+ struct netdev_saved_flags *sf;
+ struct netdev_rx *rx;
char *type; /* Port type as requested by user. */
};
bool create, struct dpif **);
static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *,
int queue_no, const struct flow *,
- uint64_t arg);
+ const struct nlattr *userdata);
static void dp_netdev_execute_actions(struct dp_netdev *,
struct ofpbuf *, struct flow *,
const struct nlattr *actions,
static struct dpif_netdev *
dpif_netdev_cast(const struct dpif *dpif)
{
- assert(dpif->dpif_class->open == dpif_netdev_open);
+ ovs_assert(dpif->dpif_class->open == dpif_netdev_open);
return CONTAINER_OF(dpif, struct dpif_netdev, dpif);
}
return 0;
}
+static bool
+dpif_netdev_class_is_dummy(const struct dpif_class *class)
+{
+ return class != &dpif_netdev_class;
+}
+
static const char *
dpif_netdev_port_open_type(const struct dpif_class *class, const char *type)
{
return strcmp(type, "internal") ? type
- : class != &dpif_netdev_class ? "dummy"
+ : dpif_netdev_class_is_dummy(class) ? "dummy"
: "tap";
}
if (error) {
return error;
}
- assert(dp != NULL);
+ ovs_assert(dp != NULL);
}
} else {
if (dp->class != class) {
dpif_netdev_close(struct dpif *dpif)
{
struct dp_netdev *dp = get_dp_netdev(dpif);
- assert(dp->open_cnt > 0);
+ ovs_assert(dp->open_cnt > 0);
if (--dp->open_cnt == 0 && dp->destroyed) {
shash_find_and_delete(&dp_netdevs, dp->name);
dp_netdev_free(dp);
do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
uint32_t port_no)
{
+ struct netdev_saved_flags *sf;
struct dp_netdev_port *port;
struct netdev *netdev;
+ struct netdev_rx *rx;
const char *open_type;
int mtu;
int error;
/* XXX reject loopback devices */
/* XXX reject non-Ethernet devices */
- error = netdev_listen(netdev);
- if (error) {
+ error = netdev_rx_open(netdev, &rx);
+ if (error
+ && !(error == EOPNOTSUPP && dpif_netdev_class_is_dummy(dp->class))) {
VLOG_ERR("%s: cannot receive packets on this network device (%s)",
devname, strerror(errno));
netdev_close(netdev);
return error;
}
- error = netdev_turn_flags_on(netdev, NETDEV_PROMISC, false);
+ error = netdev_turn_flags_on(netdev, NETDEV_PROMISC, &sf);
if (error) {
+ netdev_rx_close(rx);
netdev_close(netdev);
return error;
}
port = xmalloc(sizeof *port);
port->port_no = port_no;
port->netdev = netdev;
+ port->sf = sf;
+ port->rx = rx;
port->type = xstrdup(type);
error = netdev_get_mtu(netdev, &mtu);
- if (!error) {
+ if (!error && mtu > max_mtu) {
max_mtu = mtu;
}
}
port_no = *port_nop;
} else {
- port_no = choose_port(dp, netdev_get_name(netdev));
+ port_no = choose_port(dp, netdev_vport_get_dpif_port(netdev));
}
if (port_no >= 0) {
*port_nop = port_no;
- return do_add_port(dp, netdev_get_name(netdev),
+ return do_add_port(dp, netdev_vport_get_dpif_port(netdev),
netdev_get_type(netdev), port_no);
}
return EFBIG;
struct dp_netdev_port *port;
LIST_FOR_EACH (port, node, &dp->port_list) {
- if (!strcmp(netdev_get_name(port->netdev), devname)) {
+ if (!strcmp(netdev_vport_get_dpif_port(port->netdev), devname)) {
*portp = port;
return 0;
}
do_del_port(struct dp_netdev *dp, uint32_t port_no)
{
struct dp_netdev_port *port;
- char *name;
int error;
error = get_port_by_number(dp, port_no, &port);
dp->ports[port->port_no] = NULL;
dp->serial++;
- name = xstrdup(netdev_get_name(port->netdev));
netdev_close(port->netdev);
+ netdev_restore_flags(port->sf);
+ netdev_rx_close(port->rx);
free(port->type);
-
- free(name);
free(port);
return 0;
answer_port_query(const struct dp_netdev_port *port,
struct dpif_port *dpif_port)
{
- dpif_port->name = xstrdup(netdev_get_name(port->netdev));
+ dpif_port->name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
dpif_port->type = xstrdup(port->type);
dpif_port->port_no = port->port_no;
}
struct dp_netdev_port *port = dp->ports[port_no];
if (port) {
free(state->name);
- state->name = xstrdup(netdev_get_name(port->netdev));
+ state->name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
dpif_port->name = state->name;
dpif_port->type = port->type;
dpif_port->port_no = port->port_no;
dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
struct flow *flow)
{
- if (odp_flow_key_to_flow(key, key_len, flow)) {
+ if (odp_flow_key_to_flow(key, key_len, flow) != ODP_FIT_PERFECT) {
/* This should not happen: it indicates that odp_flow_key_from_flow()
* and odp_flow_key_to_flow() disagree on the acceptable form of a
* flow. Log the problem as an error, with enough details to enable
ofpbuf_reserve(©, DP_NETDEV_HEADROOM);
ofpbuf_put(©, execute->packet->data, execute->packet->size);
- flow_extract(©, 0, NULL, -1, &key);
+ flow_extract(©, 0, 0, NULL, -1, &key);
error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len,
&key);
if (!error) {
if (packet->size < ETH_HEADER_LEN) {
return;
}
- flow_extract(packet, 0, NULL, port->port_no, &key);
+ flow_extract(packet, 0, 0, NULL, port->port_no, &key);
flow = dp_netdev_lookup_flow(dp, &key);
if (flow) {
dp_netdev_flow_used(flow, packet);
dp->n_hit++;
} else {
dp->n_missed++;
- dp_netdev_output_userspace(dp, packet, DPIF_UC_MISS, &key, 0);
+ dp_netdev_output_userspace(dp, packet, DPIF_UC_MISS, &key, NULL);
}
}
ofpbuf_clear(&packet);
ofpbuf_reserve(&packet, DP_NETDEV_HEADROOM);
- error = netdev_recv(port->netdev, &packet);
+ error = port->rx ? netdev_rx_recv(port->rx, &packet) : EOPNOTSUPP;
if (!error) {
dp_netdev_port_input(dp, port, &packet);
} else if (error != EAGAIN && error != EOPNOTSUPP) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
- netdev_get_name(port->netdev), strerror(error));
+ netdev_vport_get_dpif_port(port->netdev),
+ strerror(error));
}
}
ofpbuf_uninit(&packet);
struct dp_netdev_port *port;
LIST_FOR_EACH (port, node, &dp->port_list) {
- netdev_recv_wait(port->netdev);
+ if (port->rx) {
+ netdev_rx_wait(port->rx);
+ }
}
}
static int
dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
- int queue_no, const struct flow *flow, uint64_t arg)
+ int queue_no, const struct flow *flow,
+ const struct nlattr *userdata)
{
struct dp_netdev_queue *q = &dp->queues[queue_no];
- struct dp_netdev_upcall *u;
- struct dpif_upcall *upcall;
- struct ofpbuf *buf;
- size_t key_len;
-
- if (q->head - q->tail >= MAX_QUEUE_LEN) {
- dp->n_lost++;
- return ENOBUFS;
- }
+ if (q->head - q->tail < MAX_QUEUE_LEN) {
+ struct dp_netdev_upcall *u = &q->upcalls[q->head++ & QUEUE_MASK];
+ struct dpif_upcall *upcall = &u->upcall;
+ struct ofpbuf *buf = &u->buf;
+ size_t buf_size;
+
+ upcall->type = queue_no;
+
+ /* Allocate buffer big enough for everything. */
+ buf_size = ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size;
+ if (userdata) {
+ buf_size += NLA_ALIGN(userdata->nla_len);
+ }
+ ofpbuf_init(buf, buf_size);
- u = &q->upcalls[q->head++ & QUEUE_MASK];
+ /* Put ODP flow. */
+ odp_flow_key_from_flow(buf, flow, flow->in_port);
+ upcall->key = buf->data;
+ upcall->key_len = buf->size;
- buf = &u->buf;
- ofpbuf_init(buf, ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size);
- odp_flow_key_from_flow(buf, flow, flow->in_port);
- key_len = buf->size;
- ofpbuf_pull(buf, key_len);
- ofpbuf_reserve(buf, 2);
- ofpbuf_put(buf, packet->data, packet->size);
+ /* Put userdata. */
+ if (userdata) {
+ upcall->userdata = ofpbuf_put(buf, userdata,
+ NLA_ALIGN(userdata->nla_len));
+ }
- upcall = &u->upcall;
- upcall->type = queue_no;
- upcall->packet = buf;
- upcall->key = buf->base;
- upcall->key_len = key_len;
- upcall->userdata = arg;
+ /* 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;
- return 0;
+ return 0;
+ } else {
+ dp->n_lost++;
+ return ENOBUFS;
+ }
}
static void
struct ofpbuf *packet, struct flow *key,
const struct nlattr *a)
{
- const struct nlattr *userdata_attr;
- uint64_t userdata;
+ const struct nlattr *userdata;
- userdata_attr = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
- userdata = userdata_attr ? nl_attr_get_u64(userdata_attr) : 0;
+ userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, key, userdata);
}
const struct ovs_key_udp *udp_key;
switch (type) {
- case OVS_KEY_ATTR_TUN_ID:
case OVS_KEY_ATTR_PRIORITY:
- case OVS_KEY_ATTR_IPV4_TUNNEL:
+ case OVS_KEY_ATTR_SKB_MARK:
+ case OVS_KEY_ATTR_TUNNEL:
/* not implemented */
break;
packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst);
break;
+ case OVS_KEY_ATTR_MPLS:
+ set_mpls_lse(packet, nl_attr_get_be32(a));
+ break;
+
case OVS_KEY_ATTR_UNSPEC:
case OVS_KEY_ATTR_ENCAP:
case OVS_KEY_ATTR_ETHERTYPE:
unsigned int left;
NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
- const struct ovs_action_push_vlan *vlan;
int type = nl_attr_type(a);
switch ((enum ovs_action_attr) type) {
dp_netdev_action_userspace(dp, packet, key, a);
break;
- case OVS_ACTION_ATTR_PUSH_VLAN:
- vlan = nl_attr_get(a);
+ case OVS_ACTION_ATTR_PUSH_VLAN: {
+ const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
eth_push_vlan(packet, vlan->vlan_tci);
break;
+ }
case OVS_ACTION_ATTR_POP_VLAN:
eth_pop_vlan(packet);
break;
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
+ const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
+ push_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse);
+ break;
+ }
+
+ case OVS_ACTION_ATTR_POP_MPLS:
+ pop_mpls(packet, nl_attr_get_be16(a));
+ break;
+
case OVS_ACTION_ATTR_SET:
execute_set_action(packet, nl_attr_get(a));
break;