From patchwork Thu Mar 28 11:38:14 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 231987 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 72FBB2C00B6 for ; Thu, 28 Mar 2013 22:41:14 +1100 (EST) Received: from localhost ([::1]:37986 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ULBCi-0000fH-MG for incoming@patchwork.ozlabs.org; Thu, 28 Mar 2013 07:41:12 -0400 Received: from eggs.gnu.org ([208.118.235.92]:43421) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ULBAo-0006za-02 for qemu-devel@nongnu.org; Thu, 28 Mar 2013 07:39:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ULBAi-0006Gt-HA for qemu-devel@nongnu.org; Thu, 28 Mar 2013 07:39:13 -0400 Received: from e7.ny.us.ibm.com ([32.97.182.137]:45687) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ULBAi-0006Go-2j for qemu-devel@nongnu.org; Thu, 28 Mar 2013 07:39:08 -0400 Received: from /spool/local by e7.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 28 Mar 2013 07:39:07 -0400 Received: from d01dlp02.pok.ibm.com (9.56.250.167) by e7.ny.us.ibm.com (192.168.1.107) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 28 Mar 2013 07:39:06 -0400 Received: from d01relay06.pok.ibm.com (d01relay06.pok.ibm.com [9.56.227.116]) by d01dlp02.pok.ibm.com (Postfix) with ESMTP id F226B6E8041 for ; Thu, 28 Mar 2013 07:39:02 -0400 (EDT) Received: from d01av04.pok.ibm.com (d01av04.pok.ibm.com [9.56.224.64]) by d01relay06.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id r2SBcZ3o27066568 for ; Thu, 28 Mar 2013 07:38:35 -0400 Received: from d01av04.pok.ibm.com (loopback [127.0.0.1]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id r2SBcYP3004908 for ; Thu, 28 Mar 2013 07:38:35 -0400 Received: from k-d941f-5.watson.ibm.com (k-d941f-5.watson.ibm.com [9.2.141.165]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id r2SBcYWJ004857 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 28 Mar 2013 07:38:34 -0400 Received: from k-d941f-5.watson.ibm.com (localhost.localdomain [127.0.0.1]) by k-d941f-5.watson.ibm.com (8.14.5/8.14.3) with ESMTP id r2SBcWdS016284; Thu, 28 Mar 2013 07:38:33 -0400 Received: (from root@localhost) by k-d941f-5.watson.ibm.com (8.14.5/8.14.5/Submit) id r2SBcWnm016283; Thu, 28 Mar 2013 07:38:32 -0400 From: Stefan Berger To: stefanb@linux.vnet.ibm.com, qemu-devel@nongnu.org, anthony@codemonkey.ws Date: Thu, 28 Mar 2013 07:38:14 -0400 Message-Id: <1364470699-16223-4-git-send-email-stefanb@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.11.7 In-Reply-To: <1364470699-16223-1-git-send-email-stefanb@linux.vnet.ibm.com> References: <1364470699-16223-1-git-send-email-stefanb@linux.vnet.ibm.com> X-TM-AS-MML: No x-cbid: 13032811-5806-0000-0000-000020829B12 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 32.97.182.137 Cc: jschopp@linux.vnet.ibm.com, coreyb@linux.vnet.ibm.com, mdroth@linux.vnet.ibm.com, mst@redhat.com Subject: [Qemu-devel] [PATCH v5 3/8] QEMUSizedBuffer X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Using the QEMUFile interface, this patch adds support functions for operating on in-memory sized buffers that can be written to or read from. Signed-off-by: Stefan Berger Signed-off-by: Joel Schopp --- include/migration/qemu-file.h | 13 ++ include/qemu-common.h | 12 ++ util/qemu-file.c | 411 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 436 insertions(+) diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index 728b6e2..0a297fc 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -24,6 +24,8 @@ #ifndef QEMU_FILE_H #define QEMU_FILE_H 1 +#include + /* This function writes a chunk of data to a file at the given position. * The pos argument can be ignored if the file is only being used for * streaming. The handler should try to write all of the data it can. @@ -58,6 +60,15 @@ typedef struct QEMUFileOps { QEMUFileGetFD *get_fd; } QEMUFileOps; +struct QEMUSizedBuffer { + struct iovec *iov; + size_t n_iov; + size_t size; /* total allocated size in all iov's */ + size_t used; /* number of used bytes */ +}; + +typedef struct QEMUSizedBuffer QEMUSizedBuffer; + QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops); QEMUFile *qemu_fopen(const char *filename, const char *mode); QEMUFile *qemu_fdopen(int fd, const char *mode); @@ -71,6 +82,8 @@ void qemu_put_byte(QEMUFile *f, int v); int qemu_read_bytes(QEMUFile *f, uint8_t *buf, int size); int qemu_peek_bytes(QEMUFile *f, uint8_t *buf, int size, size_t offset); int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size); +QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input); +const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f); static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v) { diff --git a/include/qemu-common.h b/include/qemu-common.h index 7754ee2..7794fa7 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -448,4 +448,16 @@ int uleb128_decode_small(const uint8_t *in, uint32_t *n); void hexdump(const char *buf, FILE *fp, const char *prefix, size_t size); +/* QEMU Sized Buffer */ +#include "include/migration/qemu-file.h" +QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len); +QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *); +void qsb_free(QEMUSizedBuffer *); +size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t length); +size_t qsb_get_length(const QEMUSizedBuffer *qsb); +ssize_t qsb_get_buffer(const QEMUSizedBuffer *, off_t start, size_t count, + uint8_t **buf); +ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf, + off_t pos, size_t count); + #endif diff --git a/util/qemu-file.c b/util/qemu-file.c index f8a54e7..89b0614 100644 --- a/util/qemu-file.c +++ b/util/qemu-file.c @@ -743,3 +743,414 @@ int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size) return size; } + + +#define QSB_CHUNK_SIZE (1 << 10) +#define QSB_MAX_CHUNK_SIZE (10 * QSB_CHUNK_SIZE) + +/** + * Create a QEMUSizedBuffer + * This type of buffer uses scatter-gather lists internally and + * can grow to any size. Any data array in the scatter-gather list + * can hold different amount of bytes. + * + * @buffer: Optional buffer to copy into the QSB + * @len: size of initial buffer; if @buffer is given, buffer must + * hold at least len bytes + * + * Returns a pointer to a QEMUSizedBuffer + */ +QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len) +{ + QEMUSizedBuffer *qsb; + size_t alloc_len, num_chunks, i, to_copy; + size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE) + ? QSB_MAX_CHUNK_SIZE + : QSB_CHUNK_SIZE; + + if (len == 0) { + /* we want to allocate at least one chunk */ + len = QSB_CHUNK_SIZE; + } + + num_chunks = DIV_ROUND_UP(len, chunk_size); + alloc_len = num_chunks * chunk_size; + + qsb = g_new0(QEMUSizedBuffer, 1); + qsb->iov = g_new0(struct iovec, num_chunks); + qsb->n_iov = num_chunks; + + for (i = 0; i < num_chunks; i++) { + qsb->iov[i].iov_base = g_malloc0(chunk_size); + qsb->iov[i].iov_len = chunk_size; + if (buffer) { + to_copy = (len - qsb->used) > chunk_size + ? chunk_size : (len - qsb->used); + memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy); + qsb->used += to_copy; + } + } + + qsb->size = alloc_len; + + return qsb; +} + +/** + * Free the QEMUSizedBuffer + * + * @qsb: The QEMUSizedBuffer to free + */ +void qsb_free(QEMUSizedBuffer *qsb) +{ + size_t i; + + if (!qsb) { + return; + } + + for (i = 0; i < qsb->n_iov; i++) { + g_free(qsb->iov[i].iov_base); + } + g_free(qsb->iov); + g_free(qsb); +} + +/** + * Get the number of of used bytes in the QEMUSizedBuffer + * + * @qsb: A QEMUSizedBuffer + * + * Returns the number of bytes currently used in this buffer + */ +size_t qsb_get_length(const QEMUSizedBuffer *qsb) +{ + return qsb->used; +} + +/** + * Set the length of the buffer; The primary usage of this + * function is to truncate the number of used bytes in the buffer. + * The size will not be extended beyond the current number of + * allocated bytes in the QEMUSizedBuffer. + * + * @qsb: A QEMUSizedBuffer + * @new_len : The new length of bytes in the buffer + * + * Returns the number of bytes the buffer was trucated or extended + * to. + */ +size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len) +{ + if (new_len <= qsb->size) { + qsb->used = new_len; + } else { + qsb->used = qsb->size; + } + return qsb->used; +} + +/** + * Get the iovec that holds the data for a given position @pos. + * + * @qsb: A QEMUSizedBuffer + * @pos: The index of a byte in the buffer + * @d_off: Pointer to an offset that this function will indicate + * at what position within the returned iovec the byte + * is to be found + * + * Returns the index of the iovec that holds the byte at the given + * index @pos in the byte stream; a negative number if the iovec + * for the given position @pos does not exist. + */ +static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb, + off_t pos, off_t *d_off) +{ + ssize_t i; + off_t curr = 0; + + if (pos > qsb->used) { + return -1; + } + + for (i = 0; i < qsb->n_iov; i++) { + if (curr + qsb->iov[i].iov_len > pos) { + *d_off = pos - curr; + return i; + } + curr += qsb->iov[i].iov_len; + } + return -1; +} + +/* + * Convert the QEMUSizedBuffer into a flat buffer. + * + * Note: If at all possible, try to avoid this function since it + * may unnecessarily copy memory around. + * + * @qsb: pointer to QEMUSizedBuffer + * @start : offset to start at + * @count: number of bytes to copy + * @buf: a pointer to an optional buffer to write into; the pointer may + * point to NULL in which case the buffer will be allocated; + * if buffer is provided, it must be large enough to hold @count bytes + * + * Returns the number of bytes copied into the output buffer + */ +ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start, + size_t count, uint8_t **buf) +{ + uint8_t *buffer; + const struct iovec *iov; + size_t to_copy, all_copy; + ssize_t index; + off_t s_off; + off_t d_off = 0; + char *s; + + if (start > qsb->used) { + return 0; + } + + all_copy = qsb->used - start; + if (all_copy > count) { + all_copy = count; + } else { + count = all_copy; + } + + if (*buf == NULL) { + *buf = g_malloc(all_copy); + } + buffer = *buf; + + index = qsb_get_iovec(qsb, start, &s_off); + if (index < 0) { + return 0; + } + + while (all_copy > 0) { + iov = &qsb->iov[index]; + + s = iov->iov_base; + + to_copy = iov->iov_len - s_off; + if (to_copy > all_copy) { + to_copy = all_copy; + } + memcpy(&buffer[d_off], &s[s_off], to_copy); + + d_off += to_copy; + all_copy -= to_copy; + + s_off = 0; + index++; + } + + return count; +} + +/** + * Grow the QEMUSizedBuffer to the given size and allocated + * memory for it. + * + * @qsb: A QEMUSizedBuffer + * @new_size: The new size of the buffer + * + * Returns an error code in case of memory allocation failure + * or the new size of the buffer otherwise. The returned size + * may be greater or equal to @new_size. + */ +static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size) +{ + size_t needed_chunks, i; + size_t chunk_size = QSB_CHUNK_SIZE; + + if (qsb->size < new_size) { + needed_chunks = DIV_ROUND_UP(new_size - qsb->size, + chunk_size); + + qsb->iov = g_realloc_n(qsb->iov, qsb->n_iov + needed_chunks, + sizeof(struct iovec)); + if (qsb->iov == NULL) { + return -ENOMEM; + } + + for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) { + qsb->iov[i].iov_base = g_malloc0(chunk_size); + qsb->iov[i].iov_len = chunk_size; + } + + qsb->n_iov += needed_chunks; + qsb->size += (needed_chunks * chunk_size); + } + + return qsb->size; +} + +/** + * Write into the QEMUSizedBuffer at a given position and a given + * number of bytes. This function will automatically grow the + * QEMUSizedBuffer. + * + * @qsb: A QEMUSizedBuffer + * @source: A byte array to copy data from + * @pos: The position withing the @qsb to write data to + * @size: The number of bytes to copy into the @qsb + * + * Returns an error code in case of memory allocation failure, + * @size otherwise. + */ +ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source, + off_t pos, size_t count) +{ + ssize_t rc = qsb_grow(qsb, pos + count); + size_t to_copy; + size_t all_copy = count; + const struct iovec *iov; + ssize_t index; + char *dest; + off_t d_off, s_off = 0; + + if (rc < 0) { + return rc; + } + + if (pos + count > qsb->used) { + qsb->used = pos + count; + } + + index = qsb_get_iovec(qsb, pos, &d_off); + if (index < 0) { + return 0; + } + + while (all_copy > 0) { + iov = &qsb->iov[index]; + + dest = iov->iov_base; + + to_copy = iov->iov_len - d_off; + if (to_copy > all_copy) { + to_copy = all_copy; + } + + memcpy(&dest[d_off], &source[s_off], to_copy); + + s_off += to_copy; + all_copy -= to_copy; + + d_off = 0; + index++; + } + + return count; +} + +/** + * Create an exact copy of the given QEMUSizedBuffer. + * + * @qsb : A QEMUSizedBuffer + * + * Returns a clone of @qsb + */ +QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb) +{ + QEMUSizedBuffer *out = qsb_create(NULL, qsb_get_length(qsb)); + size_t i; + off_t pos = 0; + + for (i = 0; i < qsb->n_iov; i++) { + pos += qsb_write_at(out, qsb->iov[i].iov_base, + pos, qsb->iov[i].iov_len); + } + + return out; +} + +typedef struct QEMUBuffer { + QEMUSizedBuffer *qsb; + QEMUFile *file; +} QEMUBuffer; + +static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUBuffer *s = opaque; + ssize_t len = qsb_get_length(s->qsb) - pos; + + if (len <= 0) { + return 0; + } + + if (len > size) { + len = size; + } + return qsb_get_buffer(s->qsb, pos, len, &buf); +} + +static int buf_put_buffer(void *opaque, const uint8_t *buf, + int64_t pos, int size) +{ + QEMUBuffer *s = opaque; + + return qsb_write_at(s->qsb, buf, pos, size); +} + +static int buf_close(void *opaque) +{ + QEMUBuffer *s = opaque; + + qsb_free(s->qsb); + + g_free(s); + + return 0; +} + +const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f) +{ + QEMUBuffer *p; + + qemu_fflush(f); + + p = (QEMUBuffer *)f->opaque; + + return p->qsb; +} + +static const QEMUFileOps buf_read_ops = { + .get_buffer = buf_get_buffer, + .close = buf_close +}; + +static const QEMUFileOps buf_write_ops = { + .put_buffer = buf_put_buffer, + .close = buf_close +}; + +QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input) +{ + QEMUBuffer *s; + + if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { + fprintf(stderr, "qemu_bufopen: Argument validity check failed\n"); + return NULL; + } + + s = g_malloc0(sizeof(QEMUBuffer)); + if (mode[0] == 'r') { + s->qsb = input; + } + + if (s->qsb == NULL) { + s->qsb = qsb_create(NULL, 0); + } + + if (mode[0] == 'r') { + s->file = qemu_fopen_ops(s, &buf_read_ops); + } else { + s->file = qemu_fopen_ops(s, &buf_write_ops); + } + return s->file; +}