Statistics
| Branch: | Revision:

root / buffered_file.c @ efdef95f

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 "sysemu.h"
18 39b65c2e aliguori
#include "qemu-char.h"
19 39b65c2e aliguori
#include "buffered_file.h"
20 39b65c2e aliguori
21 39b65c2e aliguori
//#define DEBUG_BUFFERED_FILE
22 39b65c2e aliguori
23 39b65c2e aliguori
typedef struct QEMUFileBuffered
24 39b65c2e aliguori
{
25 39b65c2e aliguori
    BufferedPutFunc *put_buffer;
26 39b65c2e aliguori
    BufferedPutReadyFunc *put_ready;
27 39b65c2e aliguori
    BufferedWaitForUnfreezeFunc *wait_for_unfreeze;
28 39b65c2e aliguori
    BufferedCloseFunc *close;
29 39b65c2e aliguori
    void *opaque;
30 39b65c2e aliguori
    QEMUFile *file;
31 39b65c2e aliguori
    int has_error;
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 39b65c2e aliguori
        tmp = qemu_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 39b65c2e aliguori
77 39b65c2e aliguori
    if (s->has_error) {
78 d0f2c4c6 malc
        DPRINTF("flush when error, bailing\n");
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 39b65c2e aliguori
            s->has_error = 1;
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 39b65c2e aliguori
    int offset = 0;
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 39b65c2e aliguori
    if (s->has_error) {
119 d0f2c4c6 malc
        DPRINTF("flush when error, bailing\n");
120 39b65c2e aliguori
        return -EINVAL;
121 39b65c2e aliguori
    }
122 39b65c2e aliguori
123 d0f2c4c6 malc
    DPRINTF("unfreezing output\n");
124 39b65c2e aliguori
    s->freeze_output = 0;
125 39b65c2e aliguori
126 39b65c2e aliguori
    buffered_flush(s);
127 39b65c2e aliguori
128 39b65c2e aliguori
    while (!s->freeze_output && offset < size) {
129 39b65c2e aliguori
        if (s->bytes_xfer > s->xfer_limit) {
130 d0f2c4c6 malc
            DPRINTF("transfer limit exceeded when putting\n");
131 39b65c2e aliguori
            break;
132 39b65c2e aliguori
        }
133 39b65c2e aliguori
134 39b65c2e aliguori
        ret = s->put_buffer(s->opaque, buf + offset, size - offset);
135 39b65c2e aliguori
        if (ret == -EAGAIN) {
136 d0f2c4c6 malc
            DPRINTF("backend not ready, freezing\n");
137 39b65c2e aliguori
            s->freeze_output = 1;
138 39b65c2e aliguori
            break;
139 39b65c2e aliguori
        }
140 39b65c2e aliguori
141 39b65c2e aliguori
        if (ret <= 0) {
142 d0f2c4c6 malc
            DPRINTF("error putting\n");
143 39b65c2e aliguori
            s->has_error = 1;
144 39b65c2e aliguori
            offset = -EINVAL;
145 39b65c2e aliguori
            break;
146 39b65c2e aliguori
        }
147 39b65c2e aliguori
148 d0f2c4c6 malc
        DPRINTF("put %zd byte(s)\n", ret);
149 39b65c2e aliguori
        offset += ret;
150 39b65c2e aliguori
        s->bytes_xfer += ret;
151 39b65c2e aliguori
    }
152 39b65c2e aliguori
153 39b65c2e aliguori
    if (offset >= 0) {
154 d0f2c4c6 malc
        DPRINTF("buffering %d bytes\n", size - offset);
155 39b65c2e aliguori
        buffered_append(s, buf + offset, size - offset);
156 39b65c2e aliguori
        offset = size;
157 39b65c2e aliguori
    }
158 39b65c2e aliguori
159 5e77aaa0 Avi Kivity
    if (pos == 0 && size == 0) {
160 5e77aaa0 Avi Kivity
        DPRINTF("file is ready\n");
161 5e77aaa0 Avi Kivity
        if (s->bytes_xfer <= s->xfer_limit) {
162 5e77aaa0 Avi Kivity
            DPRINTF("notifying client\n");
163 5e77aaa0 Avi Kivity
            s->put_ready(s->opaque);
164 5e77aaa0 Avi Kivity
        }
165 5e77aaa0 Avi Kivity
    }
166 5e77aaa0 Avi Kivity
167 39b65c2e aliguori
    return offset;
168 39b65c2e aliguori
}
169 39b65c2e aliguori
170 39b65c2e aliguori
static int buffered_close(void *opaque)
171 39b65c2e aliguori
{
172 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
173 39b65c2e aliguori
    int ret;
174 39b65c2e aliguori
175 d0f2c4c6 malc
    DPRINTF("closing\n");
176 39b65c2e aliguori
177 39b65c2e aliguori
    while (!s->has_error && s->buffer_size) {
178 39b65c2e aliguori
        buffered_flush(s);
179 39b65c2e aliguori
        if (s->freeze_output)
180 39b65c2e aliguori
            s->wait_for_unfreeze(s);
181 39b65c2e aliguori
    }
182 39b65c2e aliguori
183 39b65c2e aliguori
    ret = s->close(s->opaque);
184 39b65c2e aliguori
185 39b65c2e aliguori
    qemu_del_timer(s->timer);
186 39b65c2e aliguori
    qemu_free_timer(s->timer);
187 39b65c2e aliguori
    qemu_free(s->buffer);
188 39b65c2e aliguori
    qemu_free(s);
189 39b65c2e aliguori
190 39b65c2e aliguori
    return ret;
191 39b65c2e aliguori
}
192 39b65c2e aliguori
193 39b65c2e aliguori
static int buffered_rate_limit(void *opaque)
194 39b65c2e aliguori
{
195 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
196 39b65c2e aliguori
197 39b65c2e aliguori
    if (s->has_error)
198 39b65c2e aliguori
        return 0;
199 39b65c2e aliguori
200 39b65c2e aliguori
    if (s->freeze_output)
201 39b65c2e aliguori
        return 1;
202 39b65c2e aliguori
203 39b65c2e aliguori
    if (s->bytes_xfer > s->xfer_limit)
204 39b65c2e aliguori
        return 1;
205 39b65c2e aliguori
206 39b65c2e aliguori
    return 0;
207 39b65c2e aliguori
}
208 39b65c2e aliguori
209 3d002df3 Michael S. Tsirkin
static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate)
210 19629537 Glauber Costa
{
211 19629537 Glauber Costa
    QEMUFileBuffered *s = opaque;
212 19629537 Glauber Costa
    if (s->has_error)
213 19629537 Glauber Costa
        goto out;
214 19629537 Glauber Costa
215 3d002df3 Michael S. Tsirkin
    if (new_rate > SIZE_MAX) {
216 3d002df3 Michael S. Tsirkin
        new_rate = SIZE_MAX;
217 3d002df3 Michael S. Tsirkin
    }
218 3d002df3 Michael S. Tsirkin
219 19629537 Glauber Costa
    s->xfer_limit = new_rate / 10;
220 19629537 Glauber Costa
    
221 19629537 Glauber Costa
out:
222 19629537 Glauber Costa
    return s->xfer_limit;
223 19629537 Glauber Costa
}
224 19629537 Glauber Costa
225 3d002df3 Michael S. Tsirkin
static int64_t buffered_get_rate_limit(void *opaque)
226 c163b5ca lirans@il.ibm.com
{
227 c163b5ca lirans@il.ibm.com
    QEMUFileBuffered *s = opaque;
228 c163b5ca lirans@il.ibm.com
  
229 c163b5ca lirans@il.ibm.com
    return s->xfer_limit;
230 c163b5ca lirans@il.ibm.com
}
231 c163b5ca lirans@il.ibm.com
232 39b65c2e aliguori
static void buffered_rate_tick(void *opaque)
233 39b65c2e aliguori
{
234 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
235 39b65c2e aliguori
236 e447b1a6 Marcelo Tosatti
    if (s->has_error) {
237 e447b1a6 Marcelo Tosatti
        buffered_close(s);
238 39b65c2e aliguori
        return;
239 e447b1a6 Marcelo Tosatti
    }
240 39b65c2e aliguori
241 7bd427d8 Paolo Bonzini
    qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100);
242 39b65c2e aliguori
243 39b65c2e aliguori
    if (s->freeze_output)
244 39b65c2e aliguori
        return;
245 39b65c2e aliguori
246 39b65c2e aliguori
    s->bytes_xfer = 0;
247 39b65c2e aliguori
248 39b65c2e aliguori
    buffered_flush(s);
249 39b65c2e aliguori
250 39b65c2e aliguori
    /* Add some checks around this */
251 39b65c2e aliguori
    s->put_ready(s->opaque);
252 39b65c2e aliguori
}
253 39b65c2e aliguori
254 39b65c2e aliguori
QEMUFile *qemu_fopen_ops_buffered(void *opaque,
255 39b65c2e aliguori
                                  size_t bytes_per_sec,
256 39b65c2e aliguori
                                  BufferedPutFunc *put_buffer,
257 39b65c2e aliguori
                                  BufferedPutReadyFunc *put_ready,
258 39b65c2e aliguori
                                  BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
259 39b65c2e aliguori
                                  BufferedCloseFunc *close)
260 39b65c2e aliguori
{
261 39b65c2e aliguori
    QEMUFileBuffered *s;
262 39b65c2e aliguori
263 39b65c2e aliguori
    s = qemu_mallocz(sizeof(*s));
264 39b65c2e aliguori
265 39b65c2e aliguori
    s->opaque = opaque;
266 39b65c2e aliguori
    s->xfer_limit = bytes_per_sec / 10;
267 39b65c2e aliguori
    s->put_buffer = put_buffer;
268 39b65c2e aliguori
    s->put_ready = put_ready;
269 39b65c2e aliguori
    s->wait_for_unfreeze = wait_for_unfreeze;
270 39b65c2e aliguori
    s->close = close;
271 39b65c2e aliguori
272 39b65c2e aliguori
    s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
273 19629537 Glauber Costa
                             buffered_close, buffered_rate_limit,
274 c163b5ca lirans@il.ibm.com
                             buffered_set_rate_limit,
275 c163b5ca lirans@il.ibm.com
                             buffered_get_rate_limit);
276 39b65c2e aliguori
277 7bd427d8 Paolo Bonzini
    s->timer = qemu_new_timer_ms(rt_clock, buffered_rate_tick, s);
278 39b65c2e aliguori
279 7bd427d8 Paolo Bonzini
    qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100);
280 39b65c2e aliguori
281 39b65c2e aliguori
    return s->file;
282 39b65c2e aliguori
}