Statistics
| Branch: | Revision:

root / buffered_file.c @ 599825c5

History | View | Annotate | Download (6.8 kB)

1 39b65c2e aliguori
/*
2 39b65c2e aliguori
 * QEMU buffered QEMUFile
3 39b65c2e aliguori
 *
4 39b65c2e aliguori
 * Copyright IBM, Corp. 2008
5 39b65c2e aliguori
 *
6 39b65c2e aliguori
 * Authors:
7 39b65c2e aliguori
 *  Anthony Liguori   <aliguori@us.ibm.com>
8 39b65c2e aliguori
 *
9 39b65c2e aliguori
 * This work is licensed under the terms of the GNU GPL, version 2.  See
10 39b65c2e aliguori
 * the COPYING file in the top-level directory.
11 39b65c2e aliguori
 *
12 39b65c2e aliguori
 */
13 39b65c2e aliguori
14 39b65c2e aliguori
#include "qemu-common.h"
15 39b65c2e aliguori
#include "hw/hw.h"
16 39b65c2e aliguori
#include "qemu-timer.h"
17 39b65c2e aliguori
#include "qemu-char.h"
18 39b65c2e aliguori
#include "buffered_file.h"
19 39b65c2e aliguori
20 39b65c2e aliguori
//#define DEBUG_BUFFERED_FILE
21 39b65c2e aliguori
22 39b65c2e aliguori
typedef struct QEMUFileBuffered
23 39b65c2e aliguori
{
24 39b65c2e aliguori
    BufferedPutFunc *put_buffer;
25 39b65c2e aliguori
    BufferedPutReadyFunc *put_ready;
26 39b65c2e aliguori
    BufferedWaitForUnfreezeFunc *wait_for_unfreeze;
27 39b65c2e aliguori
    BufferedCloseFunc *close;
28 39b65c2e aliguori
    void *opaque;
29 39b65c2e aliguori
    QEMUFile *file;
30 39b65c2e aliguori
    int freeze_output;
31 39b65c2e aliguori
    size_t bytes_xfer;
32 39b65c2e aliguori
    size_t xfer_limit;
33 39b65c2e aliguori
    uint8_t *buffer;
34 39b65c2e aliguori
    size_t buffer_size;
35 39b65c2e aliguori
    size_t buffer_capacity;
36 39b65c2e aliguori
    QEMUTimer *timer;
37 39b65c2e aliguori
} QEMUFileBuffered;
38 39b65c2e aliguori
39 39b65c2e aliguori
#ifdef DEBUG_BUFFERED_FILE
40 d0f2c4c6 malc
#define DPRINTF(fmt, ...) \
41 39b65c2e aliguori
    do { printf("buffered-file: " fmt, ## __VA_ARGS__); } while (0)
42 39b65c2e aliguori
#else
43 d0f2c4c6 malc
#define DPRINTF(fmt, ...) \
44 39b65c2e aliguori
    do { } while (0)
45 39b65c2e aliguori
#endif
46 39b65c2e aliguori
47 39b65c2e aliguori
static void buffered_append(QEMUFileBuffered *s,
48 39b65c2e aliguori
                            const uint8_t *buf, size_t size)
49 39b65c2e aliguori
{
50 39b65c2e aliguori
    if (size > (s->buffer_capacity - s->buffer_size)) {
51 39b65c2e aliguori
        void *tmp;
52 39b65c2e aliguori
53 d0f2c4c6 malc
        DPRINTF("increasing buffer capacity from %zu by %zu\n",
54 39b65c2e aliguori
                s->buffer_capacity, size + 1024);
55 39b65c2e aliguori
56 39b65c2e aliguori
        s->buffer_capacity += size + 1024;
57 39b65c2e aliguori
58 7267c094 Anthony Liguori
        tmp = g_realloc(s->buffer, s->buffer_capacity);
59 39b65c2e aliguori
        if (tmp == NULL) {
60 39b65c2e aliguori
            fprintf(stderr, "qemu file buffer expansion failed\n");
61 39b65c2e aliguori
            exit(1);
62 39b65c2e aliguori
        }
63 39b65c2e aliguori
64 39b65c2e aliguori
        s->buffer = tmp;
65 39b65c2e aliguori
    }
66 39b65c2e aliguori
67 39b65c2e aliguori
    memcpy(s->buffer + s->buffer_size, buf, size);
68 39b65c2e aliguori
    s->buffer_size += size;
69 39b65c2e aliguori
}
70 39b65c2e aliguori
71 39b65c2e aliguori
static void buffered_flush(QEMUFileBuffered *s)
72 39b65c2e aliguori
{
73 39b65c2e aliguori
    size_t offset = 0;
74 42802d47 Juan Quintela
    int error;
75 39b65c2e aliguori
76 42802d47 Juan Quintela
    error = qemu_file_get_error(s->file);
77 42802d47 Juan Quintela
    if (error != 0) {
78 42802d47 Juan Quintela
        DPRINTF("flush when error, bailing: %s\n", strerror(-error));
79 39b65c2e aliguori
        return;
80 39b65c2e aliguori
    }
81 39b65c2e aliguori
82 d0f2c4c6 malc
    DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size);
83 39b65c2e aliguori
84 39b65c2e aliguori
    while (offset < s->buffer_size) {
85 39b65c2e aliguori
        ssize_t ret;
86 39b65c2e aliguori
87 39b65c2e aliguori
        ret = s->put_buffer(s->opaque, s->buffer + offset,
88 39b65c2e aliguori
                            s->buffer_size - offset);
89 39b65c2e aliguori
        if (ret == -EAGAIN) {
90 d0f2c4c6 malc
            DPRINTF("backend not ready, freezing\n");
91 39b65c2e aliguori
            s->freeze_output = 1;
92 39b65c2e aliguori
            break;
93 39b65c2e aliguori
        }
94 39b65c2e aliguori
95 39b65c2e aliguori
        if (ret <= 0) {
96 d0f2c4c6 malc
            DPRINTF("error flushing data, %zd\n", ret);
97 dcd1d224 Juan Quintela
            qemu_file_set_error(s->file, ret);
98 39b65c2e aliguori
            break;
99 39b65c2e aliguori
        } else {
100 d0f2c4c6 malc
            DPRINTF("flushed %zd byte(s)\n", ret);
101 39b65c2e aliguori
            offset += ret;
102 39b65c2e aliguori
        }
103 39b65c2e aliguori
    }
104 39b65c2e aliguori
105 d0f2c4c6 malc
    DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
106 39b65c2e aliguori
    memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
107 39b65c2e aliguori
    s->buffer_size -= offset;
108 39b65c2e aliguori
}
109 39b65c2e aliguori
110 39b65c2e aliguori
static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
111 39b65c2e aliguori
{
112 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
113 42802d47 Juan Quintela
    int offset = 0, error;
114 39b65c2e aliguori
    ssize_t ret;
115 39b65c2e aliguori
116 d0f2c4c6 malc
    DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos);
117 39b65c2e aliguori
118 42802d47 Juan Quintela
    error = qemu_file_get_error(s->file);
119 42802d47 Juan Quintela
    if (error) {
120 42802d47 Juan Quintela
        DPRINTF("flush when error, bailing: %s\n", strerror(-error));
121 42802d47 Juan Quintela
        return error;
122 39b65c2e aliguori
    }
123 39b65c2e aliguori
124 d0f2c4c6 malc
    DPRINTF("unfreezing output\n");
125 39b65c2e aliguori
    s->freeze_output = 0;
126 39b65c2e aliguori
127 39b65c2e aliguori
    buffered_flush(s);
128 39b65c2e aliguori
129 39b65c2e aliguori
    while (!s->freeze_output && offset < size) {
130 39b65c2e aliguori
        if (s->bytes_xfer > s->xfer_limit) {
131 d0f2c4c6 malc
            DPRINTF("transfer limit exceeded when putting\n");
132 39b65c2e aliguori
            break;
133 39b65c2e aliguori
        }
134 39b65c2e aliguori
135 39b65c2e aliguori
        ret = s->put_buffer(s->opaque, buf + offset, size - offset);
136 39b65c2e aliguori
        if (ret == -EAGAIN) {
137 d0f2c4c6 malc
            DPRINTF("backend not ready, freezing\n");
138 39b65c2e aliguori
            s->freeze_output = 1;
139 39b65c2e aliguori
            break;
140 39b65c2e aliguori
        }
141 39b65c2e aliguori
142 39b65c2e aliguori
        if (ret <= 0) {
143 d0f2c4c6 malc
            DPRINTF("error putting\n");
144 dcd1d224 Juan Quintela
            qemu_file_set_error(s->file, ret);
145 39b65c2e aliguori
            offset = -EINVAL;
146 39b65c2e aliguori
            break;
147 39b65c2e aliguori
        }
148 39b65c2e aliguori
149 d0f2c4c6 malc
        DPRINTF("put %zd byte(s)\n", ret);
150 39b65c2e aliguori
        offset += ret;
151 39b65c2e aliguori
        s->bytes_xfer += ret;
152 39b65c2e aliguori
    }
153 39b65c2e aliguori
154 39b65c2e aliguori
    if (offset >= 0) {
155 d0f2c4c6 malc
        DPRINTF("buffering %d bytes\n", size - offset);
156 39b65c2e aliguori
        buffered_append(s, buf + offset, size - offset);
157 39b65c2e aliguori
        offset = size;
158 39b65c2e aliguori
    }
159 39b65c2e aliguori
160 5e77aaa0 Avi Kivity
    if (pos == 0 && size == 0) {
161 5e77aaa0 Avi Kivity
        DPRINTF("file is ready\n");
162 5e77aaa0 Avi Kivity
        if (s->bytes_xfer <= s->xfer_limit) {
163 5e77aaa0 Avi Kivity
            DPRINTF("notifying client\n");
164 5e77aaa0 Avi Kivity
            s->put_ready(s->opaque);
165 5e77aaa0 Avi Kivity
        }
166 5e77aaa0 Avi Kivity
    }
167 5e77aaa0 Avi Kivity
168 39b65c2e aliguori
    return offset;
169 39b65c2e aliguori
}
170 39b65c2e aliguori
171 39b65c2e aliguori
static int buffered_close(void *opaque)
172 39b65c2e aliguori
{
173 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
174 39b65c2e aliguori
    int ret;
175 39b65c2e aliguori
176 d0f2c4c6 malc
    DPRINTF("closing\n");
177 39b65c2e aliguori
178 624b9cc2 Juan Quintela
    while (!qemu_file_get_error(s->file) && s->buffer_size) {
179 39b65c2e aliguori
        buffered_flush(s);
180 39b65c2e aliguori
        if (s->freeze_output)
181 db448a08 Juan Quintela
            s->wait_for_unfreeze(s->opaque);
182 39b65c2e aliguori
    }
183 39b65c2e aliguori
184 39b65c2e aliguori
    ret = s->close(s->opaque);
185 39b65c2e aliguori
186 39b65c2e aliguori
    qemu_del_timer(s->timer);
187 39b65c2e aliguori
    qemu_free_timer(s->timer);
188 7267c094 Anthony Liguori
    g_free(s->buffer);
189 7267c094 Anthony Liguori
    g_free(s);
190 39b65c2e aliguori
191 39b65c2e aliguori
    return ret;
192 39b65c2e aliguori
}
193 39b65c2e aliguori
194 4fc7d819 Juan Quintela
/*
195 4fc7d819 Juan Quintela
 * The meaning of the return values is:
196 4fc7d819 Juan Quintela
 *   0: We can continue sending
197 4fc7d819 Juan Quintela
 *   1: Time to stop
198 42802d47 Juan Quintela
 *   negative: There has been an error
199 4fc7d819 Juan Quintela
 */
200 39b65c2e aliguori
static int buffered_rate_limit(void *opaque)
201 39b65c2e aliguori
{
202 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
203 42802d47 Juan Quintela
    int ret;
204 39b65c2e aliguori
205 42802d47 Juan Quintela
    ret = qemu_file_get_error(s->file);
206 42802d47 Juan Quintela
    if (ret) {
207 42802d47 Juan Quintela
        return ret;
208 4fc7d819 Juan Quintela
    }
209 39b65c2e aliguori
    if (s->freeze_output)
210 39b65c2e aliguori
        return 1;
211 39b65c2e aliguori
212 39b65c2e aliguori
    if (s->bytes_xfer > s->xfer_limit)
213 39b65c2e aliguori
        return 1;
214 39b65c2e aliguori
215 39b65c2e aliguori
    return 0;
216 39b65c2e aliguori
}
217 39b65c2e aliguori
218 3d002df3 Michael S. Tsirkin
static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate)
219 19629537 Glauber Costa
{
220 19629537 Glauber Costa
    QEMUFileBuffered *s = opaque;
221 624b9cc2 Juan Quintela
    if (qemu_file_get_error(s->file)) {
222 19629537 Glauber Costa
        goto out;
223 624b9cc2 Juan Quintela
    }
224 3d002df3 Michael S. Tsirkin
    if (new_rate > SIZE_MAX) {
225 3d002df3 Michael S. Tsirkin
        new_rate = SIZE_MAX;
226 3d002df3 Michael S. Tsirkin
    }
227 3d002df3 Michael S. Tsirkin
228 19629537 Glauber Costa
    s->xfer_limit = new_rate / 10;
229 19629537 Glauber Costa
    
230 19629537 Glauber Costa
out:
231 19629537 Glauber Costa
    return s->xfer_limit;
232 19629537 Glauber Costa
}
233 19629537 Glauber Costa
234 3d002df3 Michael S. Tsirkin
static int64_t buffered_get_rate_limit(void *opaque)
235 c163b5ca lirans@il.ibm.com
{
236 c163b5ca lirans@il.ibm.com
    QEMUFileBuffered *s = opaque;
237 c163b5ca lirans@il.ibm.com
  
238 c163b5ca lirans@il.ibm.com
    return s->xfer_limit;
239 c163b5ca lirans@il.ibm.com
}
240 c163b5ca lirans@il.ibm.com
241 39b65c2e aliguori
static void buffered_rate_tick(void *opaque)
242 39b65c2e aliguori
{
243 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
244 39b65c2e aliguori
245 624b9cc2 Juan Quintela
    if (qemu_file_get_error(s->file)) {
246 e447b1a6 Marcelo Tosatti
        buffered_close(s);
247 39b65c2e aliguori
        return;
248 e447b1a6 Marcelo Tosatti
    }
249 39b65c2e aliguori
250 7bd427d8 Paolo Bonzini
    qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100);
251 39b65c2e aliguori
252 39b65c2e aliguori
    if (s->freeze_output)
253 39b65c2e aliguori
        return;
254 39b65c2e aliguori
255 39b65c2e aliguori
    s->bytes_xfer = 0;
256 39b65c2e aliguori
257 39b65c2e aliguori
    buffered_flush(s);
258 39b65c2e aliguori
259 39b65c2e aliguori
    /* Add some checks around this */
260 39b65c2e aliguori
    s->put_ready(s->opaque);
261 39b65c2e aliguori
}
262 39b65c2e aliguori
263 39b65c2e aliguori
QEMUFile *qemu_fopen_ops_buffered(void *opaque,
264 39b65c2e aliguori
                                  size_t bytes_per_sec,
265 39b65c2e aliguori
                                  BufferedPutFunc *put_buffer,
266 39b65c2e aliguori
                                  BufferedPutReadyFunc *put_ready,
267 39b65c2e aliguori
                                  BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
268 39b65c2e aliguori
                                  BufferedCloseFunc *close)
269 39b65c2e aliguori
{
270 39b65c2e aliguori
    QEMUFileBuffered *s;
271 39b65c2e aliguori
272 7267c094 Anthony Liguori
    s = g_malloc0(sizeof(*s));
273 39b65c2e aliguori
274 39b65c2e aliguori
    s->opaque = opaque;
275 39b65c2e aliguori
    s->xfer_limit = bytes_per_sec / 10;
276 39b65c2e aliguori
    s->put_buffer = put_buffer;
277 39b65c2e aliguori
    s->put_ready = put_ready;
278 39b65c2e aliguori
    s->wait_for_unfreeze = wait_for_unfreeze;
279 39b65c2e aliguori
    s->close = close;
280 39b65c2e aliguori
281 39b65c2e aliguori
    s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
282 19629537 Glauber Costa
                             buffered_close, buffered_rate_limit,
283 c163b5ca lirans@il.ibm.com
                             buffered_set_rate_limit,
284 c163b5ca lirans@il.ibm.com
                             buffered_get_rate_limit);
285 39b65c2e aliguori
286 7bd427d8 Paolo Bonzini
    s->timer = qemu_new_timer_ms(rt_clock, buffered_rate_tick, s);
287 39b65c2e aliguori
288 7bd427d8 Paolo Bonzini
    qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100);
289 39b65c2e aliguori
290 39b65c2e aliguori
    return s->file;
291 39b65c2e aliguori
}