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. */
};
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";
}
{
int 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;
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);
+ 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)",
error = netdev_turn_flags_on(netdev, NETDEV_PROMISC, &sf);
if (error) {
+ netdev_rx_close(rx);
netdev_close(netdev);
return error;
}
port->port_no = port_no;
port->netdev = netdev;
port->sf = sf;
+ port->rx = rx;
port->type = xstrdup(type);
error = netdev_get_mtu(netdev, &mtu);
netdev_close(port->netdev);
netdev_restore_flags(port->sf);
+ netdev_rx_close(port->rx);
free(port->type);
free(port);
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) {
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);
+ }
}
}
}
}
+#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, \
+ dpif_netdev_get_max_ports, \
+ 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_start, \
+ dpif_netdev_flow_dump_next, \
+ dpif_netdev_flow_dump_done, \
+ dpif_netdev_execute, \
+ NULL, /* operate */ \
+ dpif_netdev_recv_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,
- dpif_netdev_get_max_ports,
- 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_start,
- dpif_netdev_flow_dump_next,
- dpif_netdev_flow_dump_done,
- dpif_netdev_execute,
- NULL, /* operate */
- dpif_netdev_recv_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
dpif_dummy_register__("dummy");
}
+
/* Closes 'netdev'. */
void (*close)(struct netdev *netdev);
- \f
- /* ## ----------------- ## */
- /* ## Receiving Packets ## */
- /* ## ----------------- ## */
-
- /* The network provider interface is mostly used for inspecting and configuring
- * device "metadata", not for sending and receiving packets directly. It may
- * be impractical to implement these functions on some operating systems and
- * hardware. These functions may all be NULL in such cases.
- *
- * (However, the "dpif-netdev" implementation, which is the easiest way to
- * integrate Open vSwitch with a new operating system or hardware, does require
- * the ability to receive packets.) */
- /* Attempts to set up 'netdev' for receiving packets with ->recv().
- * Returns 0 if successful, otherwise a positive errno value. Return
+ /* Attempts to open a netdev_rx for receiving packets from 'netdev'.
+ * Returns 0 if successful, otherwise a positive errno value. Returns
* EOPNOTSUPP to indicate that the network device does not implement packet
* reception through this interface. This function may be set to null if
* it would always return EOPNOTSUPP anyhow. (This will prevent the
* network device from being usefully used by the netdev-based "userspace
- * datapath".)*/
- int (*listen)(struct netdev *netdev);
-
- /* Attempts to receive a packet from 'netdev' into the 'size' bytes in
- * 'buffer'. If successful, returns the number of bytes in the received
- * packet, otherwise a negative errno value. Returns -EAGAIN immediately
- * if no packet is ready to be received.
- *
- * Returns -EMSGSIZE, and discards the packet, if the received packet is
- * longer than 'size' bytes.
- *
- * This function can only be expected to return a packet if ->listen() has
- * been called successfully.
- *
- * May be null if not needed, such as for a network device that does not
- * implement packet reception through the 'recv' member function. */
- int (*recv)(struct netdev *netdev, void *buffer, size_t size);
-
- /* Registers with the poll loop to wake up from the next call to
- * poll_block() when a packet is ready to be received with netdev_recv() on
- * 'netdev'.
+ * datapath".)
*
- * May be null if not needed, such as for a network device that does not
- * implement packet reception through the 'recv' member function. */
- void (*recv_wait)(struct netdev *netdev);
+ * On success, the implementation must set '*rxp' to a 'netdev_rx' for
+ * 'netdev' that it has already initialized (with netdev_rx_init()). */
+ int (*rx_open)(struct netdev *netdev, struct netdev_rx **rxp);
- /* Discards all packets waiting to be received from 'netdev'.
- *
- * May be null if not needed, such as for a network device that does not
- * implement packet reception through the 'recv' member function. */
- int (*drain)(struct netdev *netdev);
- \f
/* Sends the 'size'-byte packet in 'buffer' on 'netdev'. Returns 0 if
* successful, otherwise a positive errno value. Returns EAGAIN without
* blocking if the packet cannot be queued immediately. Returns EMSGSIZE
* change, although implementations should try to avoid this. */
unsigned int (*change_seq)(const struct netdev *netdev);
};
+ \f
+ /* A data structure for capturing packets received by a network device.
+ *
+ * This structure should be treated as opaque by network device
+ * implementations. */
+ struct netdev_rx {
+ const struct netdev_rx_class *rx_class;
+ struct netdev_dev *netdev_dev;
+ };
+
+ void netdev_rx_init(struct netdev_rx *, struct netdev_dev *,
+ const struct netdev_rx_class *);
+ void netdev_rx_uninit(struct netdev_rx *);
+ struct netdev_dev *netdev_rx_get_dev(const struct netdev_rx *);
+
+ struct netdev_rx_class {
+ /* Destroys 'rx'. */
+ void (*destroy)(struct netdev_rx *rx);
+
+ /* Attempts to receive a packet from 'rx' into the 'size' bytes in
+ * 'buffer'. If successful, returns the number of bytes in the received
+ * packet, otherwise a negative errno value. Returns -EAGAIN immediately
+ * if no packet is ready to be received.
+ *
+ * Must return -EMSGSIZE, and discard the packet, if the received packet
+ * is longer than 'size' bytes. */
+ int (*recv)(struct netdev_rx *rx, void *buffer, size_t size);
+
+ /* Registers with the poll loop to wake up from the next call to
+ * poll_block() when a packet is ready to be received with netdev_rx_recv()
+ * on 'rx'. */
+ void (*wait)(struct netdev_rx *rx);
+
+ /* Discards all packets waiting to be received from 'rx'. */
+ int (*drain)(struct netdev_rx *rx);
+ };
+
+ static inline void netdev_rx_assert_class(const struct netdev_rx *rx,
+ const struct netdev_rx_class *class_)
+ {
+ ovs_assert(rx->rx_class == class_);
+ }
int netdev_register_provider(const struct netdev_class *);
int netdev_unregister_provider(const char *type);
extern const struct netdev_class netdev_bsd_class;
#endif
+extern const struct netdev_class netdev_tunnel_class;
+extern const struct netdev_class netdev_pltap_class;
+
#ifdef __cplusplus
}
#endif
netdev_register_provider(&netdev_tap_class);
netdev_register_provider(&netdev_bsd_class);
#endif
+ netdev_register_provider(&netdev_tunnel_class);
+ netdev_register_provider(&netdev_pltap_class);
}
}
}
}
- /* Attempts to set up 'netdev' for receiving packets with netdev_recv().
- * Returns 0 if successful, otherwise a positive errno value. EOPNOTSUPP
- * indicates that the network device does not implement packet reception
- * through this interface. */
int
- netdev_listen(struct netdev *netdev)
+ netdev_rx_open(struct netdev *netdev, struct netdev_rx **rxp)
{
- int (*listen)(struct netdev *);
+ struct netdev_dev *dev = netdev_get_dev(netdev);
+ int error;
- listen = netdev_get_dev(netdev)->netdev_class->listen;
- return listen ? (listen)(netdev) : EOPNOTSUPP;
+ error = (dev->netdev_class->rx_open
+ ? dev->netdev_class->rx_open(netdev, rxp)
+ : EOPNOTSUPP);
+ if (!error) {
+ ovs_assert((*rxp)->netdev_dev == dev);
+ dev->ref_cnt++;
+ } else {
+ *rxp = NULL;
+ }
+ return error;
+ }
+
+ void
+ netdev_rx_close(struct netdev_rx *rx)
+ {
+ if (rx) {
+ struct netdev_dev *dev = rx->netdev_dev;
+
+ rx->rx_class->destroy(rx);
+ netdev_dev_unref(dev);
+ }
}
- /* Attempts to receive a packet from 'netdev' into 'buffer', which the caller
- * must have initialized with sufficient room for the packet. The space
- * required to receive any packet is ETH_HEADER_LEN bytes, plus VLAN_HEADER_LEN
- * bytes, plus the device's MTU (which may be retrieved via netdev_get_mtu()).
- * (Some devices do not allow for a VLAN header, in which case VLAN_HEADER_LEN
- * need not be included.)
- *
- * This function can only be expected to return a packet if ->listen() has
- * been called successfully.
- *
- * If a packet is successfully retrieved, returns 0. In this case 'buffer' is
- * guaranteed to contain at least ETH_TOTAL_MIN bytes. Otherwise, returns a
- * positive errno value. Returns EAGAIN immediately if no packet is ready to
- * be returned.
- *
- * Some network devices may not implement support for this function. In such
- * cases this function will always return EOPNOTSUPP. */
int
- netdev_recv(struct netdev *netdev, struct ofpbuf *buffer)
+ netdev_rx_recv(struct netdev_rx *rx, struct ofpbuf *buffer)
{
- int (*recv)(struct netdev *, void *, size_t);
int retval;
ovs_assert(buffer->size == 0);
ovs_assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN);
- recv = netdev_get_dev(netdev)->netdev_class->recv;
- retval = (recv
- ? (recv)(netdev, buffer->data, ofpbuf_tailroom(buffer))
- : -EOPNOTSUPP);
+ retval = rx->rx_class->recv(rx, buffer->data, ofpbuf_tailroom(buffer));
if (retval >= 0) {
COVERAGE_INC(netdev_received);
buffer->size += retval;
}
}
- /* Registers with the poll loop to wake up from the next call to poll_block()
- * when a packet is ready to be received with netdev_recv() on 'netdev'. */
void
- netdev_recv_wait(struct netdev *netdev)
+ netdev_rx_wait(struct netdev_rx *rx)
{
- void (*recv_wait)(struct netdev *);
-
- recv_wait = netdev_get_dev(netdev)->netdev_class->recv_wait;
- if (recv_wait) {
- recv_wait(netdev);
- }
+ rx->rx_class->wait(rx);
}
- /* Discards all packets waiting to be received from 'netdev'. */
int
- netdev_drain(struct netdev *netdev)
+ netdev_rx_drain(struct netdev_rx *rx)
{
- int (*drain)(struct netdev *);
-
- drain = netdev_get_dev(netdev)->netdev_class->drain;
- return drain ? drain(netdev) : 0;
+ return rx->rx_class->drain ? rx->rx_class->drain(rx) : 0;
}
/* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive
return netdev->netdev_dev;
}
\f
- /* Restores all flags that have been saved with netdev_save_flags() and not yet
- * restored with netdev_restore_flags(). */
+ void
+ netdev_rx_init(struct netdev_rx *rx, struct netdev_dev *dev,
+ const struct netdev_rx_class *class)
+ {
+ ovs_assert(dev->ref_cnt > 0);
+ rx->rx_class = class;
+ rx->netdev_dev = dev;
+ }
+
+ void
+ netdev_rx_uninit(struct netdev_rx *rx OVS_UNUSED)
+ {
+ /* Nothing to do. */
+ }
+
+ struct netdev_dev *
+ netdev_rx_get_dev(const struct netdev_rx *rx)
+ {
+ ovs_assert(rx->netdev_dev->ref_cnt > 0);
+ return rx->netdev_dev;
+ }
+
+ const char *
+ netdev_rx_get_name(const struct netdev_rx *rx)
+ {
+ return netdev_dev_get_name(netdev_rx_get_dev(rx));
+ }
+
static void
restore_all_flags(void *aux OVS_UNUSED)
{