2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-30 22:05:19 +00:00

netdev: netdev_send accepts multiple packets

The netdev_send function has been modified to accept multiple packets, to
allow netdev providers to amortize locking and queuing costs.
This is especially true for netdev-dpdk.

Later commits exploit the new API.

Signed-off-by: Daniele Di Proietto <ddiproietto@vmware.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
This commit is contained in:
Daniele Di Proietto
2014-06-23 11:43:58 -07:00
committed by Pravin B Shelar
parent 910885540a
commit f4fd623c4c
8 changed files with 242 additions and 156 deletions

View File

@@ -1012,7 +1012,7 @@ netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dpif_packet **packets,
VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
ovs_strerror(errno), netdev_rxq_get_name(rxq_));
}
ofpbuf_delete(buffer);
dpif_packet_delete(packet);
} else {
dp_packet_pad(buffer);
packets[0] = packet;
@@ -1057,13 +1057,16 @@ netdev_linux_rxq_drain(struct netdev_rxq *rxq_)
* The kernel maintains a packet transmission queue, so the caller is not
* expected to do additional queuing of packets. */
static int
netdev_linux_send(struct netdev *netdev_, struct dpif_packet *pkt,
netdev_linux_send(struct netdev *netdev_, struct dpif_packet **pkts, int cnt,
bool may_steal)
{
const void *data = ofpbuf_data(&pkt->ofpbuf);
size_t size = ofpbuf_size(&pkt->ofpbuf);
int i;
int error = 0;
for (;;) {
/* 'i' is incremented only if there's no error */
for (i = 0; i < cnt;) {
const void *data = ofpbuf_data(&pkts[i]->ofpbuf);
size_t size = ofpbuf_size(&pkts[i]->ofpbuf);
ssize_t retval;
if (!is_tap_netdev(netdev_)) {
@@ -1113,31 +1116,41 @@ netdev_linux_send(struct netdev *netdev_, struct dpif_packet *pkt,
retval = write(netdev->tap_fd, data, size);
}
if (may_steal) {
dpif_packet_delete(pkt);
}
if (retval < 0) {
/* The Linux AF_PACKET implementation never blocks waiting for room
* for packets, instead returning ENOBUFS. Translate this into
* EAGAIN for the caller. */
if (errno == ENOBUFS) {
return EAGAIN;
} else if (errno == EINTR) {
error = errno == ENOBUFS ? EAGAIN : errno;
if (error == EINTR) {
/* continue without incrementing 'i', i.e. retry this packet */
continue;
} else if (errno != EAGAIN) {
VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
netdev_get_name(netdev_), ovs_strerror(errno));
}
return errno;
break;
} else if (retval != size) {
VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIuSIZE" bytes of "
"%"PRIuSIZE") on %s", retval, size, netdev_get_name(netdev_));
return EMSGSIZE;
} else {
return 0;
VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIuSIZE" bytes"
" of %"PRIuSIZE") on %s", retval, size,
netdev_get_name(netdev_));
error = EMSGSIZE;
break;
}
/* Process the next packet in the batch */
i++;
}
if (may_steal) {
for (i = 0; i < cnt; i++) {
dpif_packet_delete(pkts[i]);
}
}
if (error && error != EAGAIN) {
VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
netdev_get_name(netdev_), ovs_strerror(error));
}
return error;
}
/* Registers with the poll loop to wake up from the next call to poll_block()