#include "list.h"
#include "meta-flow.h"
#include "netdev.h"
+#include "netdev-dpdk.h"
#include "netdev-vport.h"
#include "netlink.h"
#include "odp-execute.h"
#define NETDEV_RULE_PRIORITY 0x8000
#define NR_THREADS 1
+/* Use per thread recirc_depth to prevent recirculation loop. */
+#define MAX_RECIRC_DEPTH 5
+DEFINE_STATIC_PER_THREAD_DATA(uint32_t, recirc_depth, 0)
/* Configuration parameters. */
enum { MAX_FLOWS = 65536 }; /* Maximum number of flows in flow table. */
odp_port_t port_no;
struct netdev *netdev;
struct netdev_saved_flags *sf;
- struct netdev_rxq *rxq;
+ struct netdev_rxq **rxq;
struct ovs_refcount ref_cnt;
char *type; /* Port type as requested by user. */
};
return class != &dpif_netdev_class;
}
+static bool
+dpif_netdev_class_is_planetlab(const struct dpif_class *class)
+{
+ return class == &dpif_planetlab_class;
+}
+
static const char *
dpif_netdev_port_open_type(const struct dpif_class *class, const char *type)
{
return strcmp(type, "internal") ? type
+ : dpif_netdev_class_is_planetlab(class) ? "pltap"
: dpif_netdev_class_is_dummy(class) ? "dummy"
: "tap";
}
{
uint32_t port_no;
- if (dp->class != &dpif_netdev_class) {
+ if (dp->class != &dpif_netdev_class &&
+ dp->class != &dpif_planetlab_class) {
const char *p;
int start_no = 0;
enum netdev_flags flags;
const char *open_type;
int error;
+ int i;
/* XXX reject devices already in some dp_netdev. */
port = xzalloc(sizeof *port);
port->port_no = port_no;
port->netdev = netdev;
+ port->rxq = xmalloc(sizeof *port->rxq * netdev_n_rxq(netdev));
port->type = xstrdup(type);
- error = netdev_rxq_open(netdev, &port->rxq);
- if (error
- && !(error == EOPNOTSUPP && dpif_netdev_class_is_dummy(dp->class))) {
- VLOG_ERR("%s: cannot receive packets on this network device (%s)",
- devname, ovs_strerror(errno));
- netdev_close(netdev);
- return error;
+ for (i = 0; i < netdev_n_rxq(netdev); i++) {
+ error = netdev_rxq_open(netdev, &port->rxq[i], i);
+ if (error
+ && !(error == EOPNOTSUPP && dpif_netdev_class_is_dummy(dp->class))) {
+ VLOG_ERR("%s: cannot receive packets on this network device (%s)",
+ devname, ovs_strerror(errno));
+ netdev_close(netdev);
+ return error;
+ }
}
error = netdev_turn_flags_on(netdev, NETDEV_PROMISC, &sf);
if (error) {
- netdev_rxq_close(port->rxq);
+ for (i = 0; i < netdev_n_rxq(netdev); i++) {
+ netdev_rxq_close(port->rxq[i]);
+ }
netdev_close(netdev);
free(port->rxq);
free(port);
port_unref(struct dp_netdev_port *port)
{
if (port && ovs_refcount_unref(&port->ref_cnt) == 1) {
+ int i;
+
netdev_close(port->netdev);
netdev_restore_flags(port->sf);
- netdev_rxq_close(port->rxq);
+
+ for (i = 0; i < netdev_n_rxq(port->netdev); i++) {
+ netdev_rxq_close(port->rxq[i]);
+ }
free(port->type);
free(port);
}
return EINVAL;
}
- /* Force unwildcard the in_port. */
- mask->in_port.odp_port = u32_to_odp(UINT32_MAX);
} else {
enum mf_field_id id;
/* No mask key, unwildcard everything except fields whose
}
}
+ /* Force unwildcard the in_port.
+ *
+ * We need to do this even in the case where we unwildcard "everything"
+ * above because "everything" only includes the 16-bit OpenFlow port number
+ * mask->in_port.ofp_port, which only covers half of the 32-bit datapath
+ * port number mask->in_port.odp_port. */
+ mask->in_port.odp_port = u32_to_odp(UINT32_MAX);
+
return 0;
}
odp_flow_key_from_flow(&buf, &netdev_flow->flow,
netdev_flow->flow.in_port.odp_port);
- *key = buf.data;
- *key_len = buf.size;
+ *key = ofpbuf_data(&buf);
+ *key_len = ofpbuf_size(&buf);
}
if (key && mask) {
odp_to_u32(wc.masks.in_port.odp_port),
SIZE_MAX);
- *mask = buf.data;
- *mask_len = buf.size;
+ *mask = ofpbuf_data(&buf);
+ *mask_len = ofpbuf_size(&buf);
}
if (actions || stats) {
struct pkt_metadata *md = &execute->md;
struct flow key;
- if (execute->packet->size < ETH_HEADER_LEN ||
- execute->packet->size > UINT16_MAX) {
+ if (ofpbuf_size(execute->packet) < ETH_HEADER_LEN ||
+ ofpbuf_size(execute->packet) > UINT16_MAX) {
return EINVAL;
}
}
\f
-inline static void
+static void
dp_netdev_process_rxq_port(struct dp_netdev *dp,
struct dp_netdev_port *port,
struct netdev_rxq *rxq)
ovs_rwlock_rdlock(&dp->port_rwlock);
HMAP_FOR_EACH (port, node, &dp->ports) {
- if (port->rxq && !netdev_is_pmd(port->netdev)) {
- dp_netdev_process_rxq_port(dp, port, port->rxq);
+ if (!netdev_is_pmd(port->netdev)) {
+ int i;
+
+ for (i = 0; i < netdev_n_rxq(port->netdev); i++) {
+ dp_netdev_process_rxq_port(dp, port, port->rxq[i]);
+ }
}
}
ovs_rwlock_rdlock(&dp->port_rwlock);
HMAP_FOR_EACH (port, node, &dp->ports) {
- if (port->rxq && !netdev_is_pmd(port->netdev)) {
- netdev_rxq_wait(port->rxq);
+ if (!netdev_is_pmd(port->netdev)) {
+ int i;
+
+ for (i = 0; i < netdev_n_rxq(port->netdev); i++) {
+ netdev_rxq_wait(port->rxq[i]);
+ }
}
}
ovs_rwlock_unlock(&dp->port_rwlock);
struct rxq_poll {
struct dp_netdev_port *port;
+ struct netdev_rxq *rx;
};
static int
HMAP_FOR_EACH (port, node, &f->dp->ports) {
if (netdev_is_pmd(port->netdev)) {
- if ((index % dp->n_pmd_threads) == id) {
- poll_list = xrealloc(poll_list, sizeof *poll_list * (poll_cnt + 1));
+ int i;
+
+ for (i = 0; i < netdev_n_rxq(port->netdev); i++) {
+ if ((index % dp->n_pmd_threads) == id) {
+ poll_list = xrealloc(poll_list, sizeof *poll_list * (poll_cnt + 1));
- port_ref(port);
- poll_list[poll_cnt++].port = port;
+ port_ref(port);
+ poll_list[poll_cnt].port = port;
+ poll_list[poll_cnt].rx = port->rxq[i];
+ poll_cnt++;
+ }
+ index++;
}
- index++;
}
}
poll_cnt = 0;
poll_list = NULL;
+ pmd_thread_setaffinity_cpu(f->id);
reload:
poll_cnt = pmd_load_queues(f, &poll_list, poll_cnt);
atomic_read(&f->change_seq, &port_seq);
int i;
for (i = 0; i < poll_cnt; i++) {
- dp_netdev_process_rxq_port(dp, poll_list[i].port, poll_list[i].port->rxq);
+ dp_netdev_process_rxq_port(dp, poll_list[i].port, poll_list[i].rx);
}
if (lc++ > 1024) {
ovs_mutex_lock(&bucket->mutex);
bucket->used = MAX(now, bucket->used);
bucket->packet_count++;
- bucket->byte_count += packet->size;
+ bucket->byte_count += ofpbuf_size(packet);
bucket->tcp_flags |= tcp_flags;
ovs_mutex_unlock(&bucket->mutex);
}
}
static void
-dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet,
- struct pkt_metadata *md)
+dp_netdev_input(struct dp_netdev *dp, struct ofpbuf *packet,
+ struct pkt_metadata *md)
+ OVS_REQ_RDLOCK(dp->port_rwlock)
{
struct dp_netdev_flow *netdev_flow;
struct flow key;
- if (packet->size < ETH_HEADER_LEN) {
+ if (ofpbuf_size(packet) < ETH_HEADER_LEN) {
ofpbuf_delete(packet);
return;
}
}
}
+static void
+dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet,
+ struct pkt_metadata *md)
+ OVS_REQ_RDLOCK(dp->port_rwlock)
+{
+ uint32_t *recirc_depth = recirc_depth_get();
+
+ *recirc_depth = 0;
+ dp_netdev_input(dp, packet, md);
+}
+
static int
dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
int queue_no, int type, const struct flow *flow,
if (userdata) {
buf_size += NLA_ALIGN(userdata->nla_len);
}
- buf_size += packet->size;
+ buf_size += ofpbuf_size(packet);
ofpbuf_init(buf, buf_size);
/* Put ODP flow. */
odp_flow_key_from_flow(buf, flow, flow->in_port.odp_port);
- upcall->key = buf->data;
- upcall->key_len = buf->size;
+ upcall->key = ofpbuf_data(buf);
+ upcall->key_len = ofpbuf_size(buf);
/* Put userdata. */
if (userdata) {
NLA_ALIGN(userdata->nla_len));
}
- upcall->packet.data = ofpbuf_put(buf, packet->data, packet->size);
- upcall->packet.size = packet->size;
+ ofpbuf_set_data(&upcall->packet,
+ ofpbuf_put(buf, ofpbuf_data(packet), ofpbuf_size(packet)));
+ ofpbuf_set_size(&upcall->packet, ofpbuf_size(packet));
seq_change(q->seq);
static void
dp_execute_cb(void *aux_, struct ofpbuf *packet,
- const struct pkt_metadata *md OVS_UNUSED,
+ struct pkt_metadata *md,
const struct nlattr *a, bool may_steal)
OVS_NO_THREAD_SAFETY_ANALYSIS
{
struct dp_netdev_execute_aux *aux = aux_;
int type = nl_attr_type(a);
struct dp_netdev_port *p;
+ uint32_t *depth = recirc_depth_get();
switch ((enum ovs_action_attr)type) {
case OVS_ACTION_ATTR_OUTPUT:
}
break;
}
+
+ case OVS_ACTION_ATTR_RECIRC:
+ if (*depth < MAX_RECIRC_DEPTH) {
+ struct pkt_metadata recirc_md = *md;
+ struct ofpbuf *recirc_packet;
+ const struct ovs_action_recirc *act;
+
+ recirc_packet = may_steal ? packet : ofpbuf_clone(packet);
+
+ act = nl_attr_get(a);
+ recirc_md.recirc_id = act->recirc_id;
+ recirc_md.dp_hash = 0;
+
+ if (act->hash_alg == OVS_RECIRC_HASH_ALG_L4) {
+ recirc_md.dp_hash = flow_hash_symmetric_l4(aux->key,
+ act->hash_bias);
+ if (!recirc_md.dp_hash) {
+ recirc_md.dp_hash = 1; /* 0 is not valid */
+ }
+ }
+
+ (*depth)++;
+ dp_netdev_input(aux->dp, recirc_packet, &recirc_md);
+ (*depth)--;
+
+ break;
+ } else {
+ VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
+ }
+ break;
+
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
}
-
}
static void
actions, actions_len, dp_execute_cb);
}
+#define DPIF_NETDEV_CLASS_FUNCTIONS \
+ dpif_netdev_enumerate, \
+ dpif_netdev_port_open_type, \
+ dpif_netdev_open, \
+ dpif_netdev_close, \
+ dpif_netdev_destroy, \
+ dpif_netdev_run, \
+ dpif_netdev_wait, \
+ dpif_netdev_get_stats, \
+ dpif_netdev_port_add, \
+ dpif_netdev_port_del, \
+ dpif_netdev_port_query_by_number, \
+ dpif_netdev_port_query_by_name, \
+ NULL, /* port_get_pid */ \
+ dpif_netdev_port_dump_start, \
+ dpif_netdev_port_dump_next, \
+ dpif_netdev_port_dump_done, \
+ dpif_netdev_port_poll, \
+ dpif_netdev_port_poll_wait, \
+ dpif_netdev_flow_get, \
+ dpif_netdev_flow_put, \
+ dpif_netdev_flow_del, \
+ dpif_netdev_flow_flush, \
+ dpif_netdev_flow_dump_state_init, \
+ dpif_netdev_flow_dump_start, \
+ dpif_netdev_flow_dump_next, \
+ NULL, \
+ dpif_netdev_flow_dump_done, \
+ dpif_netdev_flow_dump_state_uninit, \
+ dpif_netdev_execute, \
+ NULL, /* operate */ \
+ dpif_netdev_recv_set, \
+ dpif_netdev_handlers_set, \
+ dpif_netdev_queue_to_priority, \
+ dpif_netdev_recv, \
+ dpif_netdev_recv_wait, \
+ dpif_netdev_recv_purge, \
+
const struct dpif_class dpif_netdev_class = {
"netdev",
- dpif_netdev_enumerate,
- dpif_netdev_port_open_type,
- dpif_netdev_open,
- dpif_netdev_close,
- dpif_netdev_destroy,
- dpif_netdev_run,
- dpif_netdev_wait,
- dpif_netdev_get_stats,
- dpif_netdev_port_add,
- dpif_netdev_port_del,
- dpif_netdev_port_query_by_number,
- dpif_netdev_port_query_by_name,
- NULL, /* port_get_pid */
- dpif_netdev_port_dump_start,
- dpif_netdev_port_dump_next,
- dpif_netdev_port_dump_done,
- dpif_netdev_port_poll,
- dpif_netdev_port_poll_wait,
- dpif_netdev_flow_get,
- dpif_netdev_flow_put,
- dpif_netdev_flow_del,
- dpif_netdev_flow_flush,
- dpif_netdev_flow_dump_state_init,
- dpif_netdev_flow_dump_start,
- dpif_netdev_flow_dump_next,
- NULL,
- dpif_netdev_flow_dump_done,
- dpif_netdev_flow_dump_state_uninit,
- dpif_netdev_execute,
- NULL, /* operate */
- dpif_netdev_recv_set,
- dpif_netdev_handlers_set,
- dpif_netdev_queue_to_priority,
- dpif_netdev_recv,
- dpif_netdev_recv_wait,
- dpif_netdev_recv_purge,
+ DPIF_NETDEV_CLASS_FUNCTIONS
+};
+
+const struct dpif_class dpif_planetlab_class = {
+ "planetlab",
+ DPIF_NETDEV_CLASS_FUNCTIONS
};
static void
"DP PORT NEW-NUMBER",
3, 3, dpif_dummy_change_port_number, NULL);
}
+