[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 1/7] vnvram: VNVRAM bdrv support
From: |
Corey Bryant |
Subject: |
[Qemu-devel] [PATCH 1/7] vnvram: VNVRAM bdrv support |
Date: |
Thu, 23 May 2013 13:44:41 -0400 |
Provides low-level VNVRAM functionality that reads and writes data,
such as an entry's binary blob, to a drive image using the block
driver.
Signed-off-by: Corey Bryant <address@hidden>
---
Makefile.objs | 2 +
vnvram.c | 487 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
vnvram.h | 22 +++
3 files changed, 511 insertions(+), 0 deletions(-)
create mode 100644 vnvram.c
create mode 100644 vnvram.h
diff --git a/Makefile.objs b/Makefile.objs
index 286ce06..4875a94 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -76,6 +76,8 @@ common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
+common-obj-y += vnvram.o
+
######################################################################
# qapi
diff --git a/vnvram.c b/vnvram.c
new file mode 100644
index 0000000..e467198
--- /dev/null
+++ b/vnvram.c
@@ -0,0 +1,487 @@
+/*
+ * VNVRAM -- stores persistent data in image files
+ *
+ * Copyright (C) 2013 IBM Corporation
+ *
+ * Authors:
+ * Stefan Berger <address@hidden>
+ * Corey Bryant <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "vnvram.h"
+#include "block/block.h"
+
+/*
+#define VNVRAM_DEBUG
+*/
+
+#ifdef VNVRAM_DEBUG
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define VNVRAM_ENTRY_DATA \
+ VNVRAMEntryName name; /* name of entry */ \
+ uint64_t blob_offset; /* start of blob on drive */ \
+ uint32_t cur_size; /* current size of blob */ \
+ uint32_t max_size; /* max size of blob */
+
+/* The following VNVRAM information is stored in-memory */
+typedef struct VNVRAMEntry {
+ VNVRAM_ENTRY_DATA
+ QLIST_ENTRY(VNVRAMEntry) next;
+} VNVRAMEntry;
+
+struct VNVRAM {
+ char *drv_id; /* corresponds to -drive id= on command line */
+ BlockDriverState *bds; /* bds for the VNVRAM drive */
+ uint64_t end_offset; /* offset on drive where next entry will go */
+ QLIST_HEAD(entries_head, VNVRAMEntry) entries_head; /* in-memory entries */
+ QLIST_ENTRY(VNVRAM) list;
+};
+
+/* There can be multiple VNVRAMS */
+static QLIST_HEAD(, VNVRAM) vnvrams = QLIST_HEAD_INITIALIZER(vnvrams);
+
+#define VNVRAM_VERSION_1 1
+#define VNVRAM_CURRENT_VERSION VNVRAM_VERSION_1
+#define VNVRAM_MAGIC 0x4E56524D /* NVRM */
+
+/* VNVRAM drive data consists of a header followed by entries and their blobs.
+ * For example:
+ * | header | entry 1 | entry 1's blob | entry 2 | entry 2's blob | ... |
+ */
+typedef struct VNVRAMDrvHdr {
+ uint16_t version;
+ uint32_t magic;
+ uint32_t num_entries;
+} QEMU_PACKED VNVRAMDrvHdr;
+
+typedef struct VNVRAMDrvEntry {
+ VNVRAM_ENTRY_DATA
+} QEMU_PACKED VNVRAMDrvEntry;
+
+static int vnvram_drv_entry_create(VNVRAM *, VNVRAMEntry *, uint64_t,
uint32_t);
+static int vnvram_drv_entry_update(VNVRAM *, VNVRAMEntry *, uint64_t,
uint32_t);
+
+/*
+ * Macros for finding entries and their drive offsets
+ */
+#define VNVRAM_FIRST_ENTRY(vnvram) \
+ QLIST_FIRST(&(vnvram)->entries_head)
+
+#define VNVRAM_NEXT_ENTRY(cur_entry) \
+ QLIST_NEXT(cur_entry, next)
+
+#define VNVRAM_FIRST_ENTRY_OFFSET() \
+ sizeof(VNVRAMDrvHdr)
+
+#define VNVRAM_NEXT_ENTRY_OFFSET(entry) \
+ ((entry)->blob_offset + (entry)->max_size)
+
+#define VNVRAM_NEXT_AVAIL_BLOB_OFFSET(vnvram) \
+ ((vnvram)->end_offset + sizeof(VNVRAMDrvEntry))
+
+#define VNVRAM_ENTRY_OFFSET_FROM_BLOB(blob_offset) \
+ (blob_offset - sizeof(VNVRAMDrvEntry))
+
+#define VNVRAM_BLOB_OFFSET_FROM_ENTRY(entry_offset) \
+ (entry_offset + sizeof(VNVRAMDrvEntry))
+
+/************************* VNVRAM drv ********************************/
+/* Low-level VNVRAM functions that work with the drive header and */
+/* entries. */
+/*********************************************************************/
+
+/*
+ * Big-endian conversions
+ */
+static void vnvram_drv_hdr_cpu_to_be(VNVRAMDrvHdr *hdr)
+{
+ hdr->version = cpu_to_be16(hdr->version);
+ hdr->magic = cpu_to_be32(hdr->magic);
+ hdr->num_entries = cpu_to_be32(hdr->num_entries);
+}
+
+static void vnvram_drv_hdr_be_to_cpu(VNVRAMDrvHdr *hdr)
+{
+ hdr->version = be16_to_cpu(hdr->version);
+ hdr->magic = be32_to_cpu(hdr->magic);
+ hdr->num_entries = be32_to_cpu(hdr->num_entries);
+}
+
+static void vnvram_drv_entry_cpu_to_be(VNVRAMDrvEntry *drv_entry)
+{
+ drv_entry->blob_offset = cpu_to_be64(drv_entry->blob_offset);
+ drv_entry->cur_size = cpu_to_be32(drv_entry->cur_size);
+ drv_entry->max_size = cpu_to_be32(drv_entry->max_size);
+}
+
+static void vnvram_drv_entry_be_to_cpu(VNVRAMDrvEntry *drv_entry)
+{
+ drv_entry->blob_offset = be64_to_cpu(drv_entry->blob_offset);
+ drv_entry->cur_size = be32_to_cpu(drv_entry->cur_size);
+ drv_entry->max_size = be32_to_cpu(drv_entry->max_size);
+}
+
+/*
+ * Find the VNVRAM that corresponds to the specified drive ID string
+ */
+static VNVRAM *vnvram_drv_find_by_id(const char *drv_id)
+{
+ VNVRAM *vnvram;
+
+ QLIST_FOREACH(vnvram, &vnvrams, list) {
+ if (strcmp(vnvram->drv_id, drv_id) == 0) {
+ return vnvram;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Increase the drive size if it's too small to fit the VNVRAM data
+ */
+static int vnvram_drv_adjust_size(VNVRAM *vnvram)
+{
+ int rc = 0;
+ int64_t needed_size;
+
+ needed_size = 0;
+
+ if (bdrv_getlength(vnvram->bds) < needed_size) {
+ rc = bdrv_truncate(vnvram->bds, needed_size);
+ if (rc != 0) {
+ DPRINTF("%s: VNVRAM drive too small\n", __func__);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Write a header to the drive with entry count of zero
+ */
+static int vnvram_drv_hdr_create_empty(VNVRAM *vnvram)
+{
+ VNVRAMDrvHdr hdr;
+
+ hdr.version = VNVRAM_CURRENT_VERSION;
+ hdr.magic = VNVRAM_MAGIC;
+ hdr.num_entries = 0;
+
+ vnvram_drv_hdr_cpu_to_be((&hdr));
+
+ if (bdrv_pwrite(vnvram->bds, 0, (&hdr), sizeof(hdr)) != sizeof(hdr)) {
+ DPRINTF("%s: Write of header to drive failed\n", __func__);
+ return -EIO;
+ }
+
+ vnvram->end_offset = sizeof(VNVRAMDrvHdr);
+
+ return 0;
+}
+
+/*
+ * Read the header from the drive
+ */
+static int vnvram_drv_hdr_read(VNVRAM *vnvram, VNVRAMDrvHdr *hdr)
+{
+ if (bdrv_pread(vnvram->bds, 0, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
+ DPRINTF("%s: Read of header from drive failed\n", __func__);
+ return -EIO;
+ }
+
+ vnvram_drv_hdr_be_to_cpu(hdr);
+
+ return 0;
+}
+
+/*
+ * Write the header to the drive
+ */
+static int vnvram_drv_hdr_write(VNVRAM *vnvram, VNVRAMDrvHdr *hdr)
+{
+ vnvram_drv_hdr_cpu_to_be(hdr);
+
+ if (bdrv_pwrite(vnvram->bds, 0, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
+ DPRINTF("%s: Write of header to drive failed\n", __func__);
+ return -EIO;
+ }
+
+ vnvram_drv_hdr_be_to_cpu(hdr);
+
+ return 0;
+}
+
+/*
+ * Read an entry from the drive (does not include blob)
+ */
+static int vnvram_drv_entry_read(VNVRAM *vnvram, uint64_t entry_offset,
+ VNVRAMDrvEntry *drv_entry)
+{
+ if (bdrv_pread(vnvram->bds, entry_offset, drv_entry, sizeof(*drv_entry))
+ != sizeof(*drv_entry)) {
+ DPRINTF("%s: VNVRAM error reading entry from drive\n", __func__);
+ return -EIO;
+ }
+
+ vnvram_drv_entry_be_to_cpu(drv_entry);
+
+ return 0;
+}
+
+/*
+ * Write an entry to the drive (does not include blob)
+ */
+static int vnvram_drv_entry_write(VNVRAM *vnvram, uint64_t entry_offset,
+ VNVRAMDrvEntry *drv_entry)
+{
+ vnvram_drv_entry_cpu_to_be(drv_entry);
+
+ if (bdrv_pwrite(vnvram->bds, entry_offset, drv_entry, sizeof(*drv_entry))
+ != sizeof(*drv_entry)) {
+ DPRINTF("%s: VNVRAM error writing entry to drive\n", __func__);
+ return -EIO;
+ }
+
+ vnvram_drv_entry_be_to_cpu(drv_entry);
+
+ return 0;
+}
+
+/*
+ * Read an entry's blob from the drive
+ */
+static int vnvram_drv_entry_read_blob(VNVRAM *vnvram, const VNVRAMEntry *entry,
+ char **blob, uint32_t *blob_size)
+{
+ int rc = 0;
+
+ *blob = NULL;
+ *blob_size = 0;
+
+ if (entry->cur_size == 0) {
+ DPRINTF("%s: VNVRAM entry not found\n", __func__);
+ rc = -ENOENT;
+ goto err_exit;
+ }
+
+ *blob = g_malloc(entry->cur_size);
+
+ DPRINTF("%s: VNVRAM read: name=%s, blob_offset=%"PRIu64",
size=%"PRIu32"\n",
+ __func__, (char *)entry->name, entry->blob_offset,
entry->cur_size);
+
+ if (bdrv_pread(vnvram->bds, entry->blob_offset, *blob, entry->cur_size)
+ != entry->cur_size) {
+ DPRINTF("%s: VNVRAM error reading blob from drive\n", __func__);
+ rc = -EIO;
+ goto err_exit;
+ }
+
+ *blob_size = entry->cur_size;
+
+ return rc;
+
+err_exit:
+ g_free(*blob);
+ *blob = NULL;
+
+ return rc;
+}
+
+/*
+ * Write an entry's blob to the drive
+ */
+static int vnvram_drv_entry_write_blob(VNVRAM *vnvram, VNVRAMEntry *entry,
+ char *blob, uint32_t blob_size)
+{
+ int rc;
+ uint64_t blob_offset;
+
+ if (blob_size == 0 || blob_size > entry->max_size) {
+ DPRINTF("%s: Blob size is not valid for entry\n", __func__);
+ rc = -EMSGSIZE;
+ goto err_exit;
+ }
+
+ rc = vnvram_drv_adjust_size(vnvram);
+ if (rc != 0) {
+ goto err_exit;
+ }
+
+ if (entry->blob_offset == 0) {
+ /* Entry doesn't exist on the drive yet */
+ blob_offset = VNVRAM_NEXT_AVAIL_BLOB_OFFSET(vnvram);
+ } else {
+ blob_offset = entry->blob_offset;
+ }
+
+ DPRINTF("%s: VNVRAM write: name=%s, blob_offset=%"PRIu64", "
+ "size=%"PRIu32"\n", __func__, (char *)entry->name,
+ blob_offset, blob_size);
+
+ if (bdrv_pwrite(vnvram->bds, blob_offset, blob, blob_size) != blob_size) {
+ DPRINTF("%s: VNVRAM error writing blob to drive\n", __func__);
+ rc = -EIO;
+ goto err_exit;
+ }
+
+ if (entry->blob_offset == 0) {
+ /* Entry doesn't exist on the drive yet */
+ rc = vnvram_drv_entry_create(vnvram, entry,
+
VNVRAM_ENTRY_OFFSET_FROM_BLOB(blob_offset),
+ blob_size);
+ if (rc != 0) {
+ DPRINTF("%s: Unable to create VNVRAM entry\n", __func__);
+ goto err_exit;
+ }
+ } else {
+ rc = vnvram_drv_entry_update(vnvram, entry,
+
VNVRAM_ENTRY_OFFSET_FROM_BLOB(blob_offset),
+ blob_size);
+ if (rc != 0) {
+ DPRINTF("%s: Unable to update VNVRAM entry\n", __func__);
+ goto err_exit;
+ }
+ }
+
+ entry->blob_offset = blob_offset;
+ entry->cur_size = blob_size;
+
+err_exit:
+ return rc;
+}
+
+/*
+ * Create an entry and write it to the drive (does not include blob)
+ */
+static int vnvram_drv_entry_create(VNVRAM *vnvram, VNVRAMEntry *entry,
+ uint64_t entry_offset, uint32_t blob_size)
+{
+ int rc;
+ VNVRAMDrvHdr hdr;
+ VNVRAMDrvEntry *drv_entry;
+
+ drv_entry = g_new0(VNVRAMDrvEntry, 1);
+
+ pstrcpy(drv_entry->name, sizeof(drv_entry->name), (char *)entry->name);
+ drv_entry->blob_offset = VNVRAM_BLOB_OFFSET_FROM_ENTRY(entry_offset);
+ drv_entry->cur_size = blob_size;
+ drv_entry->max_size = entry->max_size;
+
+ rc = vnvram_drv_entry_write(vnvram, entry_offset, drv_entry);
+ if (rc != 0) {
+ goto err_exit;
+ }
+
+ rc = vnvram_drv_hdr_read(vnvram, (&hdr));
+ if (rc != 0) {
+ goto err_exit;
+ }
+
+ hdr.num_entries++;
+
+ rc = vnvram_drv_hdr_write(vnvram, (&hdr));
+ if (rc != 0) {
+ goto err_exit;
+ }
+
+ vnvram->end_offset = drv_entry->blob_offset + drv_entry->max_size;
+
+err_exit:
+ g_free(drv_entry);
+
+ return rc;
+}
+
+/*
+ * Update an entry on the drive (does not include blob)
+ */
+static int vnvram_drv_entry_update(VNVRAM *vnvram, VNVRAMEntry *entry,
+ uint64_t entry_offset, uint32_t blob_size)
+{
+ int rc;
+ VNVRAMDrvEntry *drv_entry;
+
+ drv_entry = g_new0(VNVRAMDrvEntry, 1);
+
+ pstrcpy(drv_entry->name, sizeof(drv_entry->name), (char *)entry->name);
+ drv_entry->blob_offset = VNVRAM_BLOB_OFFSET_FROM_ENTRY(entry_offset);
+ drv_entry->cur_size = blob_size;
+ drv_entry->max_size = entry->max_size;
+
+ rc = vnvram_drv_entry_write(vnvram, entry_offset, drv_entry);
+
+ g_free(drv_entry);
+
+ return rc;
+}
+
+/*
+ * Get all entry data from the drive (does not get blob data)
+ */
+static int vnvram_drv_entries_get(VNVRAM *vnvram, VNVRAMDrvHdr *hdr,
+ VNVRAMDrvEntry **drv_entries,
+ int *num_entries)
+{
+ int i, rc;
+ uint64_t entry_offset;
+
+ *drv_entries = NULL;
+ *num_entries = 0;
+
+ *num_entries = hdr->num_entries;
+ if (*num_entries == 0) {
+ return 0;
+ }
+
+ *drv_entries = g_malloc_n(hdr->num_entries, sizeof(VNVRAMDrvEntry));
+
+ entry_offset = VNVRAM_FIRST_ENTRY_OFFSET();
+
+ for (i = 0; i < hdr->num_entries; i++) {
+ VNVRAMDrvEntry *drv_entry = &(*drv_entries)[i];
+
+ rc = vnvram_drv_entry_read(vnvram, entry_offset, drv_entry);
+ if (rc != 0) {
+ goto err_exit;
+ }
+
+ entry_offset = VNVRAM_NEXT_ENTRY_OFFSET(drv_entry);
+ }
+
+ return 0;
+
+err_exit:
+ g_free(*drv_entries);
+ *drv_entries = NULL;
+ *num_entries = 0;
+
+ return rc;
+}
+
+/*
+ * Check if the VNVRAM drive header is valid
+ */
+static bool vnvram_drv_hdr_is_valid(VNVRAM *vnvram, VNVRAMDrvHdr *hdr)
+{
+ if (hdr->version != VNVRAM_CURRENT_VERSION) {
+ DPRINTF("%s: VNVRAM drive version not valid\n", __func__);
+ return false;
+ }
+
+ if (hdr->magic != VNVRAM_MAGIC) {
+ DPRINTF("%s: VNVRAM drive magic not valid\n", __func__);
+ return false;
+ }
+
+ return true;
+}
diff --git a/vnvram.h b/vnvram.h
new file mode 100644
index 0000000..b6d7cd7
--- /dev/null
+++ b/vnvram.h
@@ -0,0 +1,22 @@
+/*
+ * VNVRAM -- stores persistent data in image files
+ *
+ * Copyright (C) 2013 IBM Corporation
+ *
+ * Authors:
+ * Stefan Berger <address@hidden>
+ * Corey Bryant <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef _QEMU_VNVRAM_H_
+#define _QEMU_VNVRAM_H_
+
+typedef struct VNVRAM VNVRAM;
+
+#define VNVRAM_ENTRY_NAME_LENGTH 16
+typedef char VNVRAMEntryName[VNVRAM_ENTRY_NAME_LENGTH];
+
+#endif
--
1.7.1
- [Qemu-devel] [PATCH 2/7] vnvram: VNVRAM in-memory support, (continued)
- [Qemu-devel] [PATCH 2/7] vnvram: VNVRAM in-memory support, Corey Bryant, 2013/05/23
- [Qemu-devel] [PATCH 3/7] vnvram: VNVRAM bottom-half r/w scheduling support, Corey Bryant, 2013/05/23
- [Qemu-devel] [PATCH 5/7] vnvram: VNVRAM additional debug support, Corey Bryant, 2013/05/23
- [Qemu-devel] [PATCH 4/7] vnvram: VNVRAM internal APIs, Corey Bryant, 2013/05/23
- [Qemu-devel] [PATCH 6/7] main: Initialize VNVRAM, Corey Bryant, 2013/05/23
- [Qemu-devel] [PATCH 7/7] monitor: QMP/HMP support for retrieving VNVRAM details, Corey Bryant, 2013/05/23
- [Qemu-devel] [PATCH 1/7] vnvram: VNVRAM bdrv support,
Corey Bryant <=
Re: [Qemu-devel] [PATCH 0/7] VNVRAM persistent storage, Anthony Liguori, 2013/05/23
Re: [Qemu-devel] [PATCH 0/7] VNVRAM persistent storage, Stefan Hajnoczi, 2013/05/24