Statistics
| Branch: | Revision:

root / buffered_file.c @ 0ecb72a5

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