Statistics
| Branch: | Revision:

root / buffered_file.c @ 1d6198c3

History | View | Annotate | Download (5.7 kB)

1
/*
2
 * QEMU buffered QEMUFile
3
 *
4
 * Copyright IBM, Corp. 2008
5
 *
6
 * Authors:
7
 *  Anthony Liguori   <aliguori@us.ibm.com>
8
 *
9
 * This work is licensed under the terms of the GNU GPL, version 2.  See
10
 * the COPYING file in the top-level directory.
11
 *
12
 */
13

    
14
#include "qemu-common.h"
15
#include "hw/hw.h"
16
#include "qemu-timer.h"
17
#include "sysemu.h"
18
#include "qemu-char.h"
19
#include "buffered_file.h"
20

    
21
//#define DEBUG_BUFFERED_FILE
22

    
23
typedef struct QEMUFileBuffered
24
{
25
    BufferedPutFunc *put_buffer;
26
    BufferedPutReadyFunc *put_ready;
27
    BufferedWaitForUnfreezeFunc *wait_for_unfreeze;
28
    BufferedCloseFunc *close;
29
    void *opaque;
30
    QEMUFile *file;
31
    int has_error;
32
    int freeze_output;
33
    size_t bytes_xfer;
34
    size_t xfer_limit;
35
    uint8_t *buffer;
36
    size_t buffer_size;
37
    size_t buffer_capacity;
38
    QEMUTimer *timer;
39
} QEMUFileBuffered;
40

    
41
#ifdef DEBUG_BUFFERED_FILE
42
#define dprintf(fmt, ...) \
43
    do { printf("buffered-file: " fmt, ## __VA_ARGS__); } while (0)
44
#else
45
#define dprintf(fmt, ...) \
46
    do { } while (0)
47
#endif
48

    
49
static void buffered_append(QEMUFileBuffered *s,
50
                            const uint8_t *buf, size_t size)
51
{
52
    if (size > (s->buffer_capacity - s->buffer_size)) {
53
        void *tmp;
54

    
55
        dprintf("increasing buffer capacity from %ld by %ld\n",
56
                s->buffer_capacity, size + 1024);
57

    
58
        s->buffer_capacity += size + 1024;
59

    
60
        tmp = qemu_realloc(s->buffer, s->buffer_capacity);
61
        if (tmp == NULL) {
62
            fprintf(stderr, "qemu file buffer expansion failed\n");
63
            exit(1);
64
        }
65

    
66
        s->buffer = tmp;
67
    }
68

    
69
    memcpy(s->buffer + s->buffer_size, buf, size);
70
    s->buffer_size += size;
71
}
72

    
73
static void buffered_flush(QEMUFileBuffered *s)
74
{
75
    size_t offset = 0;
76

    
77
    if (s->has_error) {
78
        dprintf("flush when error, bailing\n");
79
        return;
80
    }
81

    
82
    dprintf("flushing %ld byte(s) of data\n", s->buffer_size);
83

    
84
    while (offset < s->buffer_size) {
85
        ssize_t ret;
86

    
87
        ret = s->put_buffer(s->opaque, s->buffer + offset,
88
                            s->buffer_size - offset);
89
        if (ret == -EAGAIN) {
90
            dprintf("backend not ready, freezing\n");
91
            s->freeze_output = 1;
92
            break;
93
        }
94

    
95
        if (ret <= 0) {
96
            dprintf("error flushing data, %ld\n", ret);
97
            s->has_error = 1;
98
            break;
99
        } else {
100
            dprintf("flushed %ld byte(s)\n", ret);
101
            offset += ret;
102
        }
103
    }
104

    
105
    dprintf("flushed %ld of %ld byte(s)\n", offset, s->buffer_size);
106
    memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
107
    s->buffer_size -= offset;
108
}
109

    
110
static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
111
{
112
    QEMUFileBuffered *s = opaque;
113
    int offset = 0;
114
    ssize_t ret;
115

    
116
    dprintf("putting %ld bytes at %Ld\n", size, pos);
117

    
118
    if (s->has_error) {
119
        dprintf("flush when error, bailing\n");
120
        return -EINVAL;
121
    }
122

    
123
    dprintf("unfreezing output\n");
124
    s->freeze_output = 0;
125

    
126
    buffered_flush(s);
127

    
128
    while (!s->freeze_output && offset < size) {
129
        if (s->bytes_xfer > s->xfer_limit) {
130
            dprintf("transfer limit exceeded when putting\n");
131
            break;
132
        }
133

    
134
        ret = s->put_buffer(s->opaque, buf + offset, size - offset);
135
        if (ret == -EAGAIN) {
136
            dprintf("backend not ready, freezing\n");
137
            s->freeze_output = 1;
138
            break;
139
        }
140

    
141
        if (ret <= 0) {
142
            dprintf("error putting\n");
143
            s->has_error = 1;
144
            offset = -EINVAL;
145
            break;
146
        }
147

    
148
        dprintf("put %ld byte(s)\n", ret);
149
        offset += ret;
150
        s->bytes_xfer += ret;
151
    }
152

    
153
    if (offset >= 0) {
154
        dprintf("buffering %ld bytes\n", size - offset);
155
        buffered_append(s, buf + offset, size - offset);
156
        offset = size;
157
    }
158

    
159
    return offset;
160
}
161

    
162
static int buffered_close(void *opaque)
163
{
164
    QEMUFileBuffered *s = opaque;
165
    int ret;
166

    
167
    dprintf("closing\n");
168

    
169
    while (!s->has_error && s->buffer_size) {
170
        buffered_flush(s);
171
        if (s->freeze_output)
172
            s->wait_for_unfreeze(s);
173
    }
174

    
175
    ret = s->close(s->opaque);
176

    
177
    qemu_del_timer(s->timer);
178
    qemu_free_timer(s->timer);
179
    qemu_free(s->buffer);
180
    qemu_free(s);
181

    
182
    return ret;
183
}
184

    
185
static int buffered_rate_limit(void *opaque)
186
{
187
    QEMUFileBuffered *s = opaque;
188

    
189
    if (s->has_error)
190
        return 0;
191

    
192
    if (s->freeze_output)
193
        return 1;
194

    
195
    if (s->bytes_xfer > s->xfer_limit)
196
        return 1;
197

    
198
    return 0;
199
}
200

    
201
static void buffered_rate_tick(void *opaque)
202
{
203
    QEMUFileBuffered *s = opaque;
204

    
205
    if (s->has_error)
206
        return;
207

    
208
    qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
209

    
210
    if (s->freeze_output)
211
        return;
212

    
213
    s->bytes_xfer = 0;
214

    
215
    buffered_flush(s);
216

    
217
    /* Add some checks around this */
218
    s->put_ready(s->opaque);
219
}
220

    
221
QEMUFile *qemu_fopen_ops_buffered(void *opaque,
222
                                  size_t bytes_per_sec,
223
                                  BufferedPutFunc *put_buffer,
224
                                  BufferedPutReadyFunc *put_ready,
225
                                  BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
226
                                  BufferedCloseFunc *close)
227
{
228
    QEMUFileBuffered *s;
229

    
230
    s = qemu_mallocz(sizeof(*s));
231
    if (s == NULL)
232
        return NULL;
233

    
234
    s->opaque = opaque;
235
    s->xfer_limit = bytes_per_sec / 10;
236
    s->put_buffer = put_buffer;
237
    s->put_ready = put_ready;
238
    s->wait_for_unfreeze = wait_for_unfreeze;
239
    s->close = close;
240

    
241
    s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
242
                             buffered_close, buffered_rate_limit);
243

    
244
    s->timer = qemu_new_timer(rt_clock, buffered_rate_tick, s);
245

    
246
    qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
247

    
248
    return s->file;
249
}