File:  [Qemu by Fabrice Bellard] / qemu / buffered_file.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 16:50:51 2018 UTC (3 years, 1 month ago) by root
Branches: qemu, MAIN
CVS tags: qemu0105, qemu0104, qemu0103, qemu0102, qemu0101, qemu0100, HEAD
qemu 0.10.0

    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: 
  232:     s->opaque = opaque;
  233:     s->xfer_limit = bytes_per_sec / 10;
  234:     s->put_buffer = put_buffer;
  235:     s->put_ready = put_ready;
  236:     s->wait_for_unfreeze = wait_for_unfreeze;
  237:     s->close = close;
  238: 
  239:     s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
  240:                              buffered_close, buffered_rate_limit);
  241: 
  242:     s->timer = qemu_new_timer(rt_clock, buffered_rate_tick, s);
  243: 
  244:     qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
  245: 
  246:     return s->file;
  247: }

unix.superglobalmegacorp.com