Statistics
| Branch: | Revision:

root / buffered_file.c @ 06ea77bc

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