qemu/net/filter-buffer.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015 FUJITSU LIMITED
   3 * Author: Yang Hongyang <yanghy@cn.fujitsu.com>
   4 *
   5 * This work is licensed under the terms of the GNU GPL, version 2 or
   6 * later.  See the COPYING file in the top-level directory.
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "net/filter.h"
  11#include "net/queue.h"
  12#include "qapi/error.h"
  13#include "qemu-common.h"
  14#include "qemu/timer.h"
  15#include "qemu/iov.h"
  16#include "qapi/qmp/qerror.h"
  17#include "qapi-visit.h"
  18#include "qom/object.h"
  19
  20#define TYPE_FILTER_BUFFER "filter-buffer"
  21
  22#define FILTER_BUFFER(obj) \
  23    OBJECT_CHECK(FilterBufferState, (obj), TYPE_FILTER_BUFFER)
  24
  25typedef struct FilterBufferState {
  26    NetFilterState parent_obj;
  27
  28    NetQueue *incoming_queue;
  29    uint32_t interval;
  30    QEMUTimer release_timer;
  31} FilterBufferState;
  32
  33static void filter_buffer_flush(NetFilterState *nf)
  34{
  35    FilterBufferState *s = FILTER_BUFFER(nf);
  36
  37    if (!qemu_net_queue_flush(s->incoming_queue)) {
  38        /* Unable to empty the queue, purge remaining packets */
  39        qemu_net_queue_purge(s->incoming_queue, nf->netdev);
  40    }
  41}
  42
  43static void filter_buffer_release_timer(void *opaque)
  44{
  45    NetFilterState *nf = opaque;
  46    FilterBufferState *s = FILTER_BUFFER(nf);
  47
  48    /*
  49     * Note: filter_buffer_flush() drops packets that can't be sent
  50     * TODO: We should leave them queued.  But currently there's no way
  51     * for the next filter or receiver to notify us that it can receive
  52     * more packets.
  53     */
  54    filter_buffer_flush(nf);
  55    /* Timer rearmed to fire again in s->interval microseconds. */
  56    timer_mod(&s->release_timer,
  57              qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + s->interval);
  58}
  59
  60/* filter APIs */
  61static ssize_t filter_buffer_receive_iov(NetFilterState *nf,
  62                                         NetClientState *sender,
  63                                         unsigned flags,
  64                                         const struct iovec *iov,
  65                                         int iovcnt,
  66                                         NetPacketSent *sent_cb)
  67{
  68    FilterBufferState *s = FILTER_BUFFER(nf);
  69
  70    /*
  71     * We return size when buffer a packet, the sender will take it as
  72     * a already sent packet, so sent_cb should not be called later.
  73     *
  74     * FIXME: Even if the guest can't receive packets for some reasons,
  75     * the filter can still accept packets until its internal queue is full.
  76     * For example:
  77     *   For some reason, receiver could not receive more packets
  78     * (.can_receive() returns zero). Without a filter, at most one packet
  79     * will be queued in incoming queue and sender's poll will be disabled
  80     * unit its sent_cb() was called. With a filter, it will keep receiving
  81     * the packets without caring about the receiver. This is suboptimal.
  82     * May need more thoughts (e.g keeping sent_cb).
  83     */
  84    qemu_net_queue_append_iov(s->incoming_queue, sender, flags,
  85                              iov, iovcnt, NULL);
  86    return iov_size(iov, iovcnt);
  87}
  88
  89static void filter_buffer_cleanup(NetFilterState *nf)
  90{
  91    FilterBufferState *s = FILTER_BUFFER(nf);
  92
  93    if (s->interval) {
  94        timer_del(&s->release_timer);
  95    }
  96
  97    /* flush packets */
  98    if (s->incoming_queue) {
  99        filter_buffer_flush(nf);
 100        g_free(s->incoming_queue);
 101    }
 102}
 103
 104static void filter_buffer_setup_timer(NetFilterState *nf)
 105{
 106    FilterBufferState *s = FILTER_BUFFER(nf);
 107
 108    if (s->interval) {
 109        timer_init_us(&s->release_timer, QEMU_CLOCK_VIRTUAL,
 110                      filter_buffer_release_timer, nf);
 111        /* Timer armed to fire in s->interval microseconds. */
 112        timer_mod(&s->release_timer,
 113                  qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + s->interval);
 114    }
 115}
 116
 117static void filter_buffer_setup(NetFilterState *nf, Error **errp)
 118{
 119    FilterBufferState *s = FILTER_BUFFER(nf);
 120
 121    /*
 122     * We may want to accept zero interval when VM FT solutions like MC
 123     * or COLO use this filter to release packets on demand.
 124     */
 125    if (!s->interval) {
 126        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "interval",
 127                   "a non-zero interval");
 128        return;
 129    }
 130
 131    s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf);
 132    filter_buffer_setup_timer(nf);
 133}
 134
 135static void filter_buffer_status_changed(NetFilterState *nf, Error **errp)
 136{
 137    FilterBufferState *s = FILTER_BUFFER(nf);
 138
 139    if (!nf->on) {
 140        if (s->interval) {
 141            timer_del(&s->release_timer);
 142        }
 143        filter_buffer_flush(nf);
 144    } else {
 145        filter_buffer_setup_timer(nf);
 146    }
 147}
 148
 149static void filter_buffer_class_init(ObjectClass *oc, void *data)
 150{
 151    NetFilterClass *nfc = NETFILTER_CLASS(oc);
 152
 153    nfc->setup = filter_buffer_setup;
 154    nfc->cleanup = filter_buffer_cleanup;
 155    nfc->receive_iov = filter_buffer_receive_iov;
 156    nfc->status_changed = filter_buffer_status_changed;
 157}
 158
 159static void filter_buffer_get_interval(Object *obj, Visitor *v,
 160                                       const char *name, void *opaque,
 161                                       Error **errp)
 162{
 163    FilterBufferState *s = FILTER_BUFFER(obj);
 164    uint32_t value = s->interval;
 165
 166    visit_type_uint32(v, name, &value, errp);
 167}
 168
 169static void filter_buffer_set_interval(Object *obj, Visitor *v,
 170                                       const char *name, void *opaque,
 171                                       Error **errp)
 172{
 173    FilterBufferState *s = FILTER_BUFFER(obj);
 174    Error *local_err = NULL;
 175    uint32_t value;
 176
 177    visit_type_uint32(v, name, &value, &local_err);
 178    if (local_err) {
 179        goto out;
 180    }
 181    if (!value) {
 182        error_setg(&local_err, "Property '%s.%s' requires a positive value",
 183                   object_get_typename(obj), name);
 184        goto out;
 185    }
 186    s->interval = value;
 187
 188out:
 189    error_propagate(errp, local_err);
 190}
 191
 192static void filter_buffer_init(Object *obj)
 193{
 194    object_property_add(obj, "interval", "uint32",
 195                        filter_buffer_get_interval,
 196                        filter_buffer_set_interval, NULL, NULL, NULL);
 197}
 198
 199static const TypeInfo filter_buffer_info = {
 200    .name = TYPE_FILTER_BUFFER,
 201    .parent = TYPE_NETFILTER,
 202    .class_init = filter_buffer_class_init,
 203    .instance_init = filter_buffer_init,
 204    .instance_size = sizeof(FilterBufferState),
 205};
 206
 207static void register_types(void)
 208{
 209    type_register_static(&filter_buffer_info);
 210}
 211
 212type_init(register_types);
 213