xref: /qemu/net/queue.c (revision 705df546)
1e1144d00SMark McLoughlin /*
2e1144d00SMark McLoughlin  * Copyright (c) 2003-2008 Fabrice Bellard
3e1144d00SMark McLoughlin  * Copyright (c) 2009 Red Hat, Inc.
4e1144d00SMark McLoughlin  *
5e1144d00SMark McLoughlin  * Permission is hereby granted, free of charge, to any person obtaining a copy
6e1144d00SMark McLoughlin  * of this software and associated documentation files (the "Software"), to deal
7e1144d00SMark McLoughlin  * in the Software without restriction, including without limitation the rights
8e1144d00SMark McLoughlin  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9e1144d00SMark McLoughlin  * copies of the Software, and to permit persons to whom the Software is
10e1144d00SMark McLoughlin  * furnished to do so, subject to the following conditions:
11e1144d00SMark McLoughlin  *
12e1144d00SMark McLoughlin  * The above copyright notice and this permission notice shall be included in
13e1144d00SMark McLoughlin  * all copies or substantial portions of the Software.
14e1144d00SMark McLoughlin  *
15e1144d00SMark McLoughlin  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16e1144d00SMark McLoughlin  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17e1144d00SMark McLoughlin  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18e1144d00SMark McLoughlin  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19e1144d00SMark McLoughlin  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20e1144d00SMark McLoughlin  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21e1144d00SMark McLoughlin  * THE SOFTWARE.
22e1144d00SMark McLoughlin  */
23e1144d00SMark McLoughlin 
242744d920SPeter Maydell #include "qemu/osdep.h"
25e1144d00SMark McLoughlin #include "net/queue.h"
261de7afc9SPaolo Bonzini #include "qemu/queue.h"
271422e32dSPaolo Bonzini #include "net/net.h"
28e1144d00SMark McLoughlin 
29e1144d00SMark McLoughlin /* The delivery handler may only return zero if it will call
30e1144d00SMark McLoughlin  * qemu_net_queue_flush() when it determines that it is once again able
31e1144d00SMark McLoughlin  * to deliver packets. It must also call qemu_net_queue_purge() in its
32e1144d00SMark McLoughlin  * cleanup path.
33e1144d00SMark McLoughlin  *
34e1144d00SMark McLoughlin  * If a sent callback is provided to send(), the caller must handle a
35e1144d00SMark McLoughlin  * zero return from the delivery handler by not sending any more packets
36e1144d00SMark McLoughlin  * until we have invoked the callback. Only in that case will we queue
37e1144d00SMark McLoughlin  * the packet.
38e1144d00SMark McLoughlin  *
39e1144d00SMark McLoughlin  * If a sent callback isn't provided, we just drop the packet to avoid
40e1144d00SMark McLoughlin  * unbounded queueing.
41e1144d00SMark McLoughlin  */
42e1144d00SMark McLoughlin 
43e1144d00SMark McLoughlin struct NetPacket {
44e1144d00SMark McLoughlin     QTAILQ_ENTRY(NetPacket) entry;
454e68f7a0SStefan Hajnoczi     NetClientState *sender;
46e1144d00SMark McLoughlin     unsigned flags;
47e1144d00SMark McLoughlin     int size;
48e1144d00SMark McLoughlin     NetPacketSent *sent_cb;
49f7795e40SPhilippe Mathieu-Daudé     uint8_t data[];
50e1144d00SMark McLoughlin };
51e1144d00SMark McLoughlin 
52e1144d00SMark McLoughlin struct NetQueue {
53e1144d00SMark McLoughlin     void *opaque;
547d91ddd2SLuigi Rizzo     uint32_t nq_maxlen;
557d91ddd2SLuigi Rizzo     uint32_t nq_count;
563e033a46SYang Hongyang     NetQueueDeliverFunc *deliver;
57e1144d00SMark McLoughlin 
58b58deb34SPaolo Bonzini     QTAILQ_HEAD(, NetPacket) packets;
59e1144d00SMark McLoughlin 
60e1144d00SMark McLoughlin     unsigned delivering : 1;
61e1144d00SMark McLoughlin };
62e1144d00SMark McLoughlin 
qemu_new_net_queue(NetQueueDeliverFunc * deliver,void * opaque)633e033a46SYang Hongyang NetQueue *qemu_new_net_queue(NetQueueDeliverFunc *deliver, void *opaque)
64e1144d00SMark McLoughlin {
65e1144d00SMark McLoughlin     NetQueue *queue;
66e1144d00SMark McLoughlin 
6758889fe5SMarkus Armbruster     queue = g_new0(NetQueue, 1);
68e1144d00SMark McLoughlin 
69e1144d00SMark McLoughlin     queue->opaque = opaque;
707d91ddd2SLuigi Rizzo     queue->nq_maxlen = 10000;
717d91ddd2SLuigi Rizzo     queue->nq_count = 0;
723e033a46SYang Hongyang     queue->deliver = deliver;
73e1144d00SMark McLoughlin 
74e1144d00SMark McLoughlin     QTAILQ_INIT(&queue->packets);
75e1144d00SMark McLoughlin 
76e1144d00SMark McLoughlin     queue->delivering = 0;
77e1144d00SMark McLoughlin 
78e1144d00SMark McLoughlin     return queue;
79e1144d00SMark McLoughlin }
80e1144d00SMark McLoughlin 
qemu_del_net_queue(NetQueue * queue)81e1144d00SMark McLoughlin void qemu_del_net_queue(NetQueue *queue)
82e1144d00SMark McLoughlin {
83e1144d00SMark McLoughlin     NetPacket *packet, *next;
84e1144d00SMark McLoughlin 
85e1144d00SMark McLoughlin     QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
86e1144d00SMark McLoughlin         QTAILQ_REMOVE(&queue->packets, packet, entry);
877267c094SAnthony Liguori         g_free(packet);
88e1144d00SMark McLoughlin     }
89e1144d00SMark McLoughlin 
907267c094SAnthony Liguori     g_free(queue);
91e1144d00SMark McLoughlin }
92e1144d00SMark McLoughlin 
qemu_net_queue_append(NetQueue * queue,NetClientState * sender,unsigned flags,const uint8_t * buf,size_t size,NetPacketSent * sent_cb)9306b5f36dSStefan Hajnoczi static void qemu_net_queue_append(NetQueue *queue,
944e68f7a0SStefan Hajnoczi                                   NetClientState *sender,
95e1144d00SMark McLoughlin                                   unsigned flags,
96e1144d00SMark McLoughlin                                   const uint8_t *buf,
97e1144d00SMark McLoughlin                                   size_t size,
98e1144d00SMark McLoughlin                                   NetPacketSent *sent_cb)
99e1144d00SMark McLoughlin {
100e1144d00SMark McLoughlin     NetPacket *packet;
101e1144d00SMark McLoughlin 
1027d91ddd2SLuigi Rizzo     if (queue->nq_count >= queue->nq_maxlen && !sent_cb) {
1037d91ddd2SLuigi Rizzo         return; /* drop if queue full and no callback */
1047d91ddd2SLuigi Rizzo     }
1057267c094SAnthony Liguori     packet = g_malloc(sizeof(NetPacket) + size);
106e1144d00SMark McLoughlin     packet->sender = sender;
107e1144d00SMark McLoughlin     packet->flags = flags;
108e1144d00SMark McLoughlin     packet->size = size;
109e1144d00SMark McLoughlin     packet->sent_cb = sent_cb;
110e1144d00SMark McLoughlin     memcpy(packet->data, buf, size);
111e1144d00SMark McLoughlin 
1127d91ddd2SLuigi Rizzo     queue->nq_count++;
113e1144d00SMark McLoughlin     QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
114e1144d00SMark McLoughlin }
115e1144d00SMark McLoughlin 
qemu_net_queue_append_iov(NetQueue * queue,NetClientState * sender,unsigned flags,const struct iovec * iov,int iovcnt,NetPacketSent * sent_cb)116b68c7f76SYang Hongyang void qemu_net_queue_append_iov(NetQueue *queue,
1174e68f7a0SStefan Hajnoczi                                NetClientState *sender,
118e1144d00SMark McLoughlin                                unsigned flags,
119e1144d00SMark McLoughlin                                const struct iovec *iov,
120e1144d00SMark McLoughlin                                int iovcnt,
121e1144d00SMark McLoughlin                                NetPacketSent *sent_cb)
122e1144d00SMark McLoughlin {
123e1144d00SMark McLoughlin     NetPacket *packet;
124e1144d00SMark McLoughlin     size_t max_len = 0;
125e1144d00SMark McLoughlin     int i;
126e1144d00SMark McLoughlin 
1277d91ddd2SLuigi Rizzo     if (queue->nq_count >= queue->nq_maxlen && !sent_cb) {
1287d91ddd2SLuigi Rizzo         return; /* drop if queue full and no callback */
1297d91ddd2SLuigi Rizzo     }
130e1144d00SMark McLoughlin     for (i = 0; i < iovcnt; i++) {
131e1144d00SMark McLoughlin         max_len += iov[i].iov_len;
132e1144d00SMark McLoughlin     }
133e1144d00SMark McLoughlin 
1347267c094SAnthony Liguori     packet = g_malloc(sizeof(NetPacket) + max_len);
135e1144d00SMark McLoughlin     packet->sender = sender;
136e1144d00SMark McLoughlin     packet->sent_cb = sent_cb;
137e1144d00SMark McLoughlin     packet->flags = flags;
138e1144d00SMark McLoughlin     packet->size = 0;
139e1144d00SMark McLoughlin 
140e1144d00SMark McLoughlin     for (i = 0; i < iovcnt; i++) {
141e1144d00SMark McLoughlin         size_t len = iov[i].iov_len;
142e1144d00SMark McLoughlin 
143e1144d00SMark McLoughlin         memcpy(packet->data + packet->size, iov[i].iov_base, len);
144e1144d00SMark McLoughlin         packet->size += len;
145e1144d00SMark McLoughlin     }
146e1144d00SMark McLoughlin 
1477d91ddd2SLuigi Rizzo     queue->nq_count++;
148e1144d00SMark McLoughlin     QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
149e1144d00SMark McLoughlin }
150e1144d00SMark McLoughlin 
qemu_net_queue_deliver(NetQueue * queue,NetClientState * sender,unsigned flags,const uint8_t * data,size_t size)151e1144d00SMark McLoughlin static ssize_t qemu_net_queue_deliver(NetQueue *queue,
1524e68f7a0SStefan Hajnoczi                                       NetClientState *sender,
153e1144d00SMark McLoughlin                                       unsigned flags,
154e1144d00SMark McLoughlin                                       const uint8_t *data,
155e1144d00SMark McLoughlin                                       size_t size)
156e1144d00SMark McLoughlin {
157e1144d00SMark McLoughlin     ssize_t ret = -1;
158fefe2a78SYang Hongyang     struct iovec iov = {
159fefe2a78SYang Hongyang         .iov_base = (void *)data,
160fefe2a78SYang Hongyang         .iov_len = size
161fefe2a78SYang Hongyang     };
162e1144d00SMark McLoughlin 
163e1144d00SMark McLoughlin     queue->delivering = 1;
1643e033a46SYang Hongyang     ret = queue->deliver(sender, flags, &iov, 1, queue->opaque);
165e1144d00SMark McLoughlin     queue->delivering = 0;
166e1144d00SMark McLoughlin 
167e1144d00SMark McLoughlin     return ret;
168e1144d00SMark McLoughlin }
169e1144d00SMark McLoughlin 
qemu_net_queue_deliver_iov(NetQueue * queue,NetClientState * sender,unsigned flags,const struct iovec * iov,int iovcnt)170e1144d00SMark McLoughlin static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue,
1714e68f7a0SStefan Hajnoczi                                           NetClientState *sender,
172e1144d00SMark McLoughlin                                           unsigned flags,
173e1144d00SMark McLoughlin                                           const struct iovec *iov,
174e1144d00SMark McLoughlin                                           int iovcnt)
175e1144d00SMark McLoughlin {
176e1144d00SMark McLoughlin     ssize_t ret = -1;
177e1144d00SMark McLoughlin 
178e1144d00SMark McLoughlin     queue->delivering = 1;
1793e033a46SYang Hongyang     ret = queue->deliver(sender, flags, iov, iovcnt, queue->opaque);
180e1144d00SMark McLoughlin     queue->delivering = 0;
181e1144d00SMark McLoughlin 
182e1144d00SMark McLoughlin     return ret;
183e1144d00SMark McLoughlin }
184e1144d00SMark McLoughlin 
qemu_net_queue_receive(NetQueue * queue,const uint8_t * data,size_t size)185*705df546SJason Wang ssize_t qemu_net_queue_receive(NetQueue *queue,
186*705df546SJason Wang                                const uint8_t *data,
187*705df546SJason Wang                                size_t size)
188*705df546SJason Wang {
189*705df546SJason Wang     if (queue->delivering) {
190*705df546SJason Wang         return 0;
191*705df546SJason Wang     }
192*705df546SJason Wang 
193*705df546SJason Wang     return qemu_net_queue_deliver(queue, NULL, 0, data, size);
194*705df546SJason Wang }
195*705df546SJason Wang 
qemu_net_queue_receive_iov(NetQueue * queue,const struct iovec * iov,int iovcnt)196*705df546SJason Wang ssize_t qemu_net_queue_receive_iov(NetQueue *queue,
197*705df546SJason Wang                                    const struct iovec *iov,
198*705df546SJason Wang                                    int iovcnt)
199*705df546SJason Wang {
200*705df546SJason Wang     if (queue->delivering) {
201*705df546SJason Wang         return 0;
202*705df546SJason Wang     }
203*705df546SJason Wang 
204*705df546SJason Wang     return qemu_net_queue_deliver_iov(queue, NULL, 0, iov, iovcnt);
205*705df546SJason Wang }
206*705df546SJason Wang 
qemu_net_queue_send(NetQueue * queue,NetClientState * sender,unsigned flags,const uint8_t * data,size_t size,NetPacketSent * sent_cb)207e1144d00SMark McLoughlin ssize_t qemu_net_queue_send(NetQueue *queue,
2084e68f7a0SStefan Hajnoczi                             NetClientState *sender,
209e1144d00SMark McLoughlin                             unsigned flags,
210e1144d00SMark McLoughlin                             const uint8_t *data,
211e1144d00SMark McLoughlin                             size_t size,
212e1144d00SMark McLoughlin                             NetPacketSent *sent_cb)
213e1144d00SMark McLoughlin {
214e1144d00SMark McLoughlin     ssize_t ret;
215e1144d00SMark McLoughlin 
216691a4f3aSZhi Yong Wu     if (queue->delivering || !qemu_can_send_packet(sender)) {
21706b5f36dSStefan Hajnoczi         qemu_net_queue_append(queue, sender, flags, data, size, sent_cb);
21806b5f36dSStefan Hajnoczi         return 0;
219e1144d00SMark McLoughlin     }
220e1144d00SMark McLoughlin 
221e1144d00SMark McLoughlin     ret = qemu_net_queue_deliver(queue, sender, flags, data, size);
222839f368fSMark McLoughlin     if (ret == 0) {
223e1144d00SMark McLoughlin         qemu_net_queue_append(queue, sender, flags, data, size, sent_cb);
224e1144d00SMark McLoughlin         return 0;
225e1144d00SMark McLoughlin     }
226e1144d00SMark McLoughlin 
227e1144d00SMark McLoughlin     qemu_net_queue_flush(queue);
228e1144d00SMark McLoughlin 
229e1144d00SMark McLoughlin     return ret;
230e1144d00SMark McLoughlin }
231e1144d00SMark McLoughlin 
qemu_net_queue_send_iov(NetQueue * queue,NetClientState * sender,unsigned flags,const struct iovec * iov,int iovcnt,NetPacketSent * sent_cb)232e1144d00SMark McLoughlin ssize_t qemu_net_queue_send_iov(NetQueue *queue,
2334e68f7a0SStefan Hajnoczi                                 NetClientState *sender,
234e1144d00SMark McLoughlin                                 unsigned flags,
235e1144d00SMark McLoughlin                                 const struct iovec *iov,
236e1144d00SMark McLoughlin                                 int iovcnt,
237e1144d00SMark McLoughlin                                 NetPacketSent *sent_cb)
238e1144d00SMark McLoughlin {
239e1144d00SMark McLoughlin     ssize_t ret;
240e1144d00SMark McLoughlin 
241691a4f3aSZhi Yong Wu     if (queue->delivering || !qemu_can_send_packet(sender)) {
24206b5f36dSStefan Hajnoczi         qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, sent_cb);
24306b5f36dSStefan Hajnoczi         return 0;
244e1144d00SMark McLoughlin     }
245e1144d00SMark McLoughlin 
246e1144d00SMark McLoughlin     ret = qemu_net_queue_deliver_iov(queue, sender, flags, iov, iovcnt);
247839f368fSMark McLoughlin     if (ret == 0) {
248e1144d00SMark McLoughlin         qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, sent_cb);
249e1144d00SMark McLoughlin         return 0;
250e1144d00SMark McLoughlin     }
251e1144d00SMark McLoughlin 
252e1144d00SMark McLoughlin     qemu_net_queue_flush(queue);
253e1144d00SMark McLoughlin 
254e1144d00SMark McLoughlin     return ret;
255e1144d00SMark McLoughlin }
256e1144d00SMark McLoughlin 
qemu_net_queue_purge(NetQueue * queue,NetClientState * from)2574e68f7a0SStefan Hajnoczi void qemu_net_queue_purge(NetQueue *queue, NetClientState *from)
258e1144d00SMark McLoughlin {
259e1144d00SMark McLoughlin     NetPacket *packet, *next;
260e1144d00SMark McLoughlin 
261e1144d00SMark McLoughlin     QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
262e1144d00SMark McLoughlin         if (packet->sender == from) {
263e1144d00SMark McLoughlin             QTAILQ_REMOVE(&queue->packets, packet, entry);
2647d91ddd2SLuigi Rizzo             queue->nq_count--;
26507d80846SMichael S. Tsirkin             if (packet->sent_cb) {
26607d80846SMichael S. Tsirkin                 packet->sent_cb(packet->sender, 0);
26707d80846SMichael S. Tsirkin             }
2687267c094SAnthony Liguori             g_free(packet);
269e1144d00SMark McLoughlin         }
270e1144d00SMark McLoughlin     }
271e1144d00SMark McLoughlin }
272e1144d00SMark McLoughlin 
qemu_net_queue_flush(NetQueue * queue)273987a9b48SPaolo Bonzini bool qemu_net_queue_flush(NetQueue *queue)
274e1144d00SMark McLoughlin {
27522dc8663SJason Wang     if (queue->delivering)
27622dc8663SJason Wang         return false;
27722dc8663SJason Wang 
278e1144d00SMark McLoughlin     while (!QTAILQ_EMPTY(&queue->packets)) {
279e1144d00SMark McLoughlin         NetPacket *packet;
280e1144d00SMark McLoughlin         int ret;
281e1144d00SMark McLoughlin 
282e1144d00SMark McLoughlin         packet = QTAILQ_FIRST(&queue->packets);
283e1144d00SMark McLoughlin         QTAILQ_REMOVE(&queue->packets, packet, entry);
2847d91ddd2SLuigi Rizzo         queue->nq_count--;
285e1144d00SMark McLoughlin 
286e1144d00SMark McLoughlin         ret = qemu_net_queue_deliver(queue,
287e1144d00SMark McLoughlin                                      packet->sender,
288e1144d00SMark McLoughlin                                      packet->flags,
289e1144d00SMark McLoughlin                                      packet->data,
290e1144d00SMark McLoughlin                                      packet->size);
291839f368fSMark McLoughlin         if (ret == 0) {
2927d91ddd2SLuigi Rizzo             queue->nq_count++;
293e1144d00SMark McLoughlin             QTAILQ_INSERT_HEAD(&queue->packets, packet, entry);
294987a9b48SPaolo Bonzini             return false;
295e1144d00SMark McLoughlin         }
296e1144d00SMark McLoughlin 
297e1144d00SMark McLoughlin         if (packet->sent_cb) {
298e1144d00SMark McLoughlin             packet->sent_cb(packet->sender, ret);
299e1144d00SMark McLoughlin         }
300e1144d00SMark McLoughlin 
3017267c094SAnthony Liguori         g_free(packet);
302e1144d00SMark McLoughlin     }
303987a9b48SPaolo Bonzini     return true;
304e1144d00SMark McLoughlin }
305