Files
xemu/net/queue.c
T

305 lines
8.6 KiB
C
Raw Normal View History

2009-10-08 19:58:31 +01:00
/*
* Copyright (c) 2003-2008 Fabrice Bellard
* Copyright (c) 2009 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
2016-01-29 17:50:00 +00:00
#include "qemu/osdep.h"
2009-10-23 17:52:16 +01:00
#include "net/queue.h"
2012-12-17 18:20:00 +01:00
#include "qemu/queue.h"
2012-10-24 08:43:34 +02:00
#include "net/net.h"
2009-10-08 19:58:31 +01:00
/* The delivery handler may only return zero if it will call
* qemu_net_queue_flush() when it determines that it is once again able
* to deliver packets. It must also call qemu_net_queue_purge() in its
* cleanup path.
*
* If a sent callback is provided to send(), the caller must handle a
* zero return from the delivery handler by not sending any more packets
* until we have invoked the callback. Only in that case will we queue
* the packet.
*
* If a sent callback isn't provided, we just drop the packet to avoid
* unbounded queueing.
*/
struct NetPacket {
QTAILQ_ENTRY(NetPacket) entry;
NetClientState *sender;
unsigned flags;
2009-10-08 19:58:31 +01:00
int size;
NetPacketSent *sent_cb;
uint8_t data[];
2009-10-08 19:58:31 +01:00
};
struct NetQueue {
void *opaque;
2013-02-05 18:29:09 +01:00
uint32_t nq_maxlen;
uint32_t nq_count;
2015-10-07 11:52:17 +08:00
NetQueueDeliverFunc *deliver;
2009-10-08 19:58:31 +01:00
QTAILQ_HEAD(, NetPacket) packets;
2009-10-08 19:58:31 +01:00
unsigned delivering : 1;
};
2015-10-07 11:52:17 +08:00
NetQueue *qemu_new_net_queue(NetQueueDeliverFunc *deliver, void *opaque)
2009-10-08 19:58:31 +01:00
{
NetQueue *queue;
queue = g_new0(NetQueue, 1);
2009-10-08 19:58:31 +01:00
queue->opaque = opaque;
2013-02-05 18:29:09 +01:00
queue->nq_maxlen = 10000;
queue->nq_count = 0;
2015-10-07 11:52:17 +08:00
queue->deliver = deliver;
2009-10-08 19:58:31 +01:00
QTAILQ_INIT(&queue->packets);
queue->delivering = 0;
return queue;
}
void qemu_del_net_queue(NetQueue *queue)
{
NetPacket *packet, *next;
QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
QTAILQ_REMOVE(&queue->packets, packet, entry);
g_free(packet);
2009-10-08 19:58:31 +01:00
}
g_free(queue);
2009-10-08 19:58:31 +01:00
}
2012-08-20 13:35:23 +01:00
static void qemu_net_queue_append(NetQueue *queue,
NetClientState *sender,
unsigned flags,
const uint8_t *buf,
size_t size,
NetPacketSent *sent_cb)
2009-10-08 19:58:31 +01:00
{
NetPacket *packet;
2013-02-05 18:29:09 +01:00
if (queue->nq_count >= queue->nq_maxlen && !sent_cb) {
return; /* drop if queue full and no callback */
}
packet = g_malloc(sizeof(NetPacket) + size);
2009-10-08 19:58:31 +01:00
packet->sender = sender;
packet->flags = flags;
2009-10-08 19:58:31 +01:00
packet->size = size;
packet->sent_cb = sent_cb;
memcpy(packet->data, buf, size);
2013-02-05 18:29:09 +01:00
queue->nq_count++;
2009-10-08 19:58:31 +01:00
QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
}
2015-10-07 11:52:20 +08:00
void qemu_net_queue_append_iov(NetQueue *queue,
NetClientState *sender,
unsigned flags,
const struct iovec *iov,
int iovcnt,
NetPacketSent *sent_cb)
2009-10-08 19:58:31 +01:00
{
NetPacket *packet;
size_t max_len = 0;
int i;
2013-02-05 18:29:09 +01:00
if (queue->nq_count >= queue->nq_maxlen && !sent_cb) {
return; /* drop if queue full and no callback */
}
2009-10-08 19:58:31 +01:00
for (i = 0; i < iovcnt; i++) {
max_len += iov[i].iov_len;
}
packet = g_malloc(sizeof(NetPacket) + max_len);
2009-10-08 19:58:31 +01:00
packet->sender = sender;
packet->sent_cb = sent_cb;
packet->flags = flags;
2009-10-08 19:58:31 +01:00
packet->size = 0;
for (i = 0; i < iovcnt; i++) {
size_t len = iov[i].iov_len;
memcpy(packet->data + packet->size, iov[i].iov_base, len);
packet->size += len;
}
2013-02-05 18:29:09 +01:00
queue->nq_count++;
2009-10-08 19:58:31 +01:00
QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
}
static ssize_t qemu_net_queue_deliver(NetQueue *queue,
NetClientState *sender,
unsigned flags,
2009-10-08 19:58:31 +01:00
const uint8_t *data,
size_t size)
{
ssize_t ret = -1;
struct iovec iov = {
.iov_base = (void *)data,
.iov_len = size
};
2009-10-08 19:58:31 +01:00
queue->delivering = 1;
2015-10-07 11:52:17 +08:00
ret = queue->deliver(sender, flags, &iov, 1, queue->opaque);
2009-10-08 19:58:31 +01:00
queue->delivering = 0;
return ret;
}
static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue,
NetClientState *sender,
unsigned flags,
2009-10-08 19:58:31 +01:00
const struct iovec *iov,
int iovcnt)
{
ssize_t ret = -1;
queue->delivering = 1;
2015-10-07 11:52:17 +08:00
ret = queue->deliver(sender, flags, iov, iovcnt, queue->opaque);
2009-10-08 19:58:31 +01:00
queue->delivering = 0;
return ret;
}
2021-02-24 11:44:36 +08:00
ssize_t qemu_net_queue_receive(NetQueue *queue,
const uint8_t *data,
size_t size)
{
if (queue->delivering) {
return 0;
}
return qemu_net_queue_deliver(queue, NULL, 0, data, size);
}
ssize_t qemu_net_queue_receive_iov(NetQueue *queue,
const struct iovec *iov,
int iovcnt)
{
if (queue->delivering) {
return 0;
}
return qemu_net_queue_deliver_iov(queue, NULL, 0, iov, iovcnt);
}
2009-10-08 19:58:31 +01:00
ssize_t qemu_net_queue_send(NetQueue *queue,
NetClientState *sender,
unsigned flags,
2009-10-08 19:58:31 +01:00
const uint8_t *data,
size_t size,
NetPacketSent *sent_cb)
{
ssize_t ret;
if (queue->delivering || !qemu_can_send_packet(sender)) {
2012-08-20 13:35:23 +01:00
qemu_net_queue_append(queue, sender, flags, data, size, sent_cb);
return 0;
2009-10-08 19:58:31 +01:00
}
ret = qemu_net_queue_deliver(queue, sender, flags, data, size);
if (ret == 0) {
qemu_net_queue_append(queue, sender, flags, data, size, sent_cb);
2009-10-08 19:58:31 +01:00
return 0;
}
qemu_net_queue_flush(queue);
return ret;
}
ssize_t qemu_net_queue_send_iov(NetQueue *queue,
NetClientState *sender,
unsigned flags,
2009-10-08 19:58:31 +01:00
const struct iovec *iov,
int iovcnt,
NetPacketSent *sent_cb)
{
ssize_t ret;
if (queue->delivering || !qemu_can_send_packet(sender)) {
2012-08-20 13:35:23 +01:00
qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, sent_cb);
return 0;
2009-10-08 19:58:31 +01:00
}
ret = qemu_net_queue_deliver_iov(queue, sender, flags, iov, iovcnt);
if (ret == 0) {
qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, sent_cb);
2009-10-08 19:58:31 +01:00
return 0;
}
qemu_net_queue_flush(queue);
return ret;
}
void qemu_net_queue_purge(NetQueue *queue, NetClientState *from)
2009-10-08 19:58:31 +01:00
{
NetPacket *packet, *next;
QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
if (packet->sender == from) {
QTAILQ_REMOVE(&queue->packets, packet, entry);
2013-02-05 18:29:09 +01:00
queue->nq_count--;
2014-09-04 11:39:10 +03:00
if (packet->sent_cb) {
packet->sent_cb(packet->sender, 0);
}
g_free(packet);
2009-10-08 19:58:31 +01:00
}
}
}
2012-08-09 16:45:55 +02:00
bool qemu_net_queue_flush(NetQueue *queue)
2009-10-08 19:58:31 +01:00
{
2020-07-22 16:57:46 +08:00
if (queue->delivering)
return false;
2009-10-08 19:58:31 +01:00
while (!QTAILQ_EMPTY(&queue->packets)) {
NetPacket *packet;
int ret;
packet = QTAILQ_FIRST(&queue->packets);
QTAILQ_REMOVE(&queue->packets, packet, entry);
2013-02-05 18:29:09 +01:00
queue->nq_count--;
2009-10-08 19:58:31 +01:00
ret = qemu_net_queue_deliver(queue,
packet->sender,
packet->flags,
2009-10-08 19:58:31 +01:00
packet->data,
packet->size);
if (ret == 0) {
2013-02-05 18:29:09 +01:00
queue->nq_count++;
2009-10-08 19:58:31 +01:00
QTAILQ_INSERT_HEAD(&queue->packets, packet, entry);
2012-08-09 16:45:55 +02:00
return false;
2009-10-08 19:58:31 +01:00
}
if (packet->sent_cb) {
packet->sent_cb(packet->sender, ret);
}
g_free(packet);
2009-10-08 19:58:31 +01:00
}
2012-08-09 16:45:55 +02:00
return true;
2009-10-08 19:58:31 +01:00
}