Statistics
| Branch: | Revision:

root / buffered_file.c @ 434254aa

History | View | Annotate | Download (6.1 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 39b65c2e aliguori
    return offset;
160 39b65c2e aliguori
}
161 39b65c2e aliguori
162 39b65c2e aliguori
static int buffered_close(void *opaque)
163 39b65c2e aliguori
{
164 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
165 39b65c2e aliguori
    int ret;
166 39b65c2e aliguori
167 d0f2c4c6 malc
    DPRINTF("closing\n");
168 39b65c2e aliguori
169 39b65c2e aliguori
    while (!s->has_error && s->buffer_size) {
170 39b65c2e aliguori
        buffered_flush(s);
171 39b65c2e aliguori
        if (s->freeze_output)
172 39b65c2e aliguori
            s->wait_for_unfreeze(s);
173 39b65c2e aliguori
    }
174 39b65c2e aliguori
175 39b65c2e aliguori
    ret = s->close(s->opaque);
176 39b65c2e aliguori
177 39b65c2e aliguori
    qemu_del_timer(s->timer);
178 39b65c2e aliguori
    qemu_free_timer(s->timer);
179 39b65c2e aliguori
    qemu_free(s->buffer);
180 39b65c2e aliguori
    qemu_free(s);
181 39b65c2e aliguori
182 39b65c2e aliguori
    return ret;
183 39b65c2e aliguori
}
184 39b65c2e aliguori
185 39b65c2e aliguori
static int buffered_rate_limit(void *opaque)
186 39b65c2e aliguori
{
187 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
188 39b65c2e aliguori
189 39b65c2e aliguori
    if (s->has_error)
190 39b65c2e aliguori
        return 0;
191 39b65c2e aliguori
192 39b65c2e aliguori
    if (s->freeze_output)
193 39b65c2e aliguori
        return 1;
194 39b65c2e aliguori
195 39b65c2e aliguori
    if (s->bytes_xfer > s->xfer_limit)
196 39b65c2e aliguori
        return 1;
197 39b65c2e aliguori
198 39b65c2e aliguori
    return 0;
199 39b65c2e aliguori
}
200 39b65c2e aliguori
201 19629537 Glauber Costa
static size_t buffered_set_rate_limit(void *opaque, size_t new_rate)
202 19629537 Glauber Costa
{
203 19629537 Glauber Costa
    QEMUFileBuffered *s = opaque;
204 19629537 Glauber Costa
205 19629537 Glauber Costa
    if (s->has_error)
206 19629537 Glauber Costa
        goto out;
207 19629537 Glauber Costa
208 19629537 Glauber Costa
    s->xfer_limit = new_rate / 10;
209 19629537 Glauber Costa
    
210 19629537 Glauber Costa
out:
211 19629537 Glauber Costa
    return s->xfer_limit;
212 19629537 Glauber Costa
}
213 19629537 Glauber Costa
214 c163b5ca lirans@il.ibm.com
static size_t buffered_get_rate_limit(void *opaque)
215 c163b5ca lirans@il.ibm.com
{
216 c163b5ca lirans@il.ibm.com
    QEMUFileBuffered *s = opaque;
217 c163b5ca lirans@il.ibm.com
  
218 c163b5ca lirans@il.ibm.com
    return s->xfer_limit;
219 c163b5ca lirans@il.ibm.com
}
220 c163b5ca lirans@il.ibm.com
221 39b65c2e aliguori
static void buffered_rate_tick(void *opaque)
222 39b65c2e aliguori
{
223 39b65c2e aliguori
    QEMUFileBuffered *s = opaque;
224 39b65c2e aliguori
225 39b65c2e aliguori
    if (s->has_error)
226 39b65c2e aliguori
        return;
227 39b65c2e aliguori
228 39b65c2e aliguori
    qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
229 39b65c2e aliguori
230 39b65c2e aliguori
    if (s->freeze_output)
231 39b65c2e aliguori
        return;
232 39b65c2e aliguori
233 39b65c2e aliguori
    s->bytes_xfer = 0;
234 39b65c2e aliguori
235 39b65c2e aliguori
    buffered_flush(s);
236 39b65c2e aliguori
237 39b65c2e aliguori
    /* Add some checks around this */
238 39b65c2e aliguori
    s->put_ready(s->opaque);
239 39b65c2e aliguori
}
240 39b65c2e aliguori
241 39b65c2e aliguori
QEMUFile *qemu_fopen_ops_buffered(void *opaque,
242 39b65c2e aliguori
                                  size_t bytes_per_sec,
243 39b65c2e aliguori
                                  BufferedPutFunc *put_buffer,
244 39b65c2e aliguori
                                  BufferedPutReadyFunc *put_ready,
245 39b65c2e aliguori
                                  BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
246 39b65c2e aliguori
                                  BufferedCloseFunc *close)
247 39b65c2e aliguori
{
248 39b65c2e aliguori
    QEMUFileBuffered *s;
249 39b65c2e aliguori
250 39b65c2e aliguori
    s = qemu_mallocz(sizeof(*s));
251 39b65c2e aliguori
252 39b65c2e aliguori
    s->opaque = opaque;
253 39b65c2e aliguori
    s->xfer_limit = bytes_per_sec / 10;
254 39b65c2e aliguori
    s->put_buffer = put_buffer;
255 39b65c2e aliguori
    s->put_ready = put_ready;
256 39b65c2e aliguori
    s->wait_for_unfreeze = wait_for_unfreeze;
257 39b65c2e aliguori
    s->close = close;
258 39b65c2e aliguori
259 39b65c2e aliguori
    s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
260 19629537 Glauber Costa
                             buffered_close, buffered_rate_limit,
261 c163b5ca lirans@il.ibm.com
                             buffered_set_rate_limit,
262 c163b5ca lirans@il.ibm.com
                             buffered_get_rate_limit);
263 39b65c2e aliguori
264 39b65c2e aliguori
    s->timer = qemu_new_timer(rt_clock, buffered_rate_tick, s);
265 39b65c2e aliguori
266 39b65c2e aliguori
    qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
267 39b65c2e aliguori
268 39b65c2e aliguori
    return s->file;
269 39b65c2e aliguori
}