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:
committed by
Pravin B Shelar
parent
910885540a
commit
f4fd623c4c
@@ -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()
|
||||
|
Reference in New Issue
Block a user