Provides VNVRAM APIs that can be used by other areas of QEMU to provide persistent storage.
Signed-off-by: Corey Bryant <cor...@linux.vnet.ibm.com> --- vnvram.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vnvram.h | 14 +++ 2 files changed, 280 insertions(+), 0 deletions(-) diff --git a/vnvram.c b/vnvram.c index 4157482..357923d 100644 --- a/vnvram.c +++ b/vnvram.c @@ -15,6 +15,7 @@ #include "block/block.h" #include "monitor/monitor.h" #include "qemu/thread.h" +#include "sysemu/sysemu.h" /* #define VNVRAM_DEBUG @@ -821,3 +822,268 @@ static int vnvram_rwrequest_schedule(VNVRAMRWRequest *rwr) return rc; } + +/************************* VNVRAM APIs *******************************/ +/* VNVRAM APIs that can be used by QEMU to provide persistent storage*/ +/*********************************************************************/ + +/* + * Initialize VNVRAM + * + * This must be called before any other APIs. + */ +int vnvram_init(void) +{ + qemu_mutex_init(&vnvram_rwrequests_mutex); + vnvram_bh = qemu_bh_new(vnvram_rwrequest_callback, NULL); + DPRINTF("%s: VNVRAM initialized\n", __func__); + + return 0; +} + +/* + * Create a VNVRAM instance + * + * The VNVRAM instance will use the drive with the corresponding ID as + * its persistent storage device. + */ +VNVRAM *vnvram_create(const char *drv_id, bool fail_on_invalid, int *errcode) +{ + int rc; + VNVRAM *vnvram = NULL; + VNVRAMDrvHdr hdr; + BlockDriverState *bds; + + *errcode = 0; + + if (runstate_check(RUN_STATE_INMIGRATE)) { + qerror_report(QERR_MIGRATION_ACTIVE); + rc = -EAGAIN; + goto err_exit; + } + + bds = bdrv_find(drv_id); + if (!bds) { + qerror_report(QERR_DEVICE_NOT_FOUND, drv_id); + rc = -ENOENT; + goto err_exit; + } + + if (bdrv_is_read_only(bds)) { + qerror_report(QERR_DEVICE_IS_READ_ONLY, drv_id); + rc = -EPERM; + goto err_exit; + } + + bdrv_lock_medium(bds, true); + + vnvram = vnvram_drv_find_by_id(drv_id); + if (vnvram) { + /* This VNVRAM was already created - sucess */ + return vnvram; + } + + vnvram = g_new0(VNVRAM, 1); + vnvram->drv_id = g_strdup(drv_id); + vnvram->bds = bds; + + QLIST_INIT(&vnvram->entries_head); + + rc = vnvram_drv_adjust_size(vnvram); + if (rc != 0) { + qerror_report(QERR_IO_ERROR); + goto err_exit; + } + + rc = vnvram_drv_hdr_read(vnvram, (&hdr)); + if (rc != 0) { + qerror_report(QERR_IO_ERROR); + goto err_exit; + } + + if (vnvram_drv_hdr_is_valid(vnvram, (&hdr))) { + rc = vnvram_sync_from_drv(vnvram, (&hdr)); + if (rc != 0) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM drive sync error"); + goto err_exit; + } + + if (vnvram_entries_are_valid(vnvram, bdrv_getlength(vnvram->bds))) { + /* Sync'd VNVRAM drive looks good - success */ + goto exit; + } + } + + /* Drive data looks invalid. Could be encrypted and we didn't get key? */ + if (bdrv_is_encrypted(vnvram->bds)) { + DPRINTF("%s: VNVRAM drive is encrypted\n", __func__); + } + + /* Either fail or reformat the drive. */ + if (fail_on_invalid) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM drive not valid"); + rc = -EIO; + goto err_exit; + } + + rc = vnvram_drv_hdr_create_empty(vnvram); + if (rc != 0) { + qerror_report(QERR_IO_ERROR); + goto err_exit; + } + +exit: + QLIST_INSERT_HEAD(&vnvrams, vnvram, list); + DPRINTF("%s: VNVRAM with drive '%s' created\n", __func__, vnvram->drv_id); + + return vnvram; + +err_exit: + if (vnvram) { + g_free(vnvram->drv_id); + } + g_free(vnvram); + *errcode = rc; + + return NULL; +} + +/* + * Register a VNVRAM entry + * + * The entry information will not be flushed to the drive until the next + * write. + */ +int vnvram_register_entry(VNVRAM *vnvram, const VNVRAMEntryName *entry_name, + uint32_t max_size) +{ + if (!vnvram_exists(vnvram)) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM has not been created"); + return -EPERM; + } + + return vnvram_register_entry_internal(vnvram, entry_name, 0, 0, max_size); +} + +/* + * Deregister a VNVRAM entry + */ +int vnvram_deregister_entry(VNVRAM *vnvram, const VNVRAMEntryName *entry_name) +{ + VNVRAMEntry *entry; + + if (!vnvram_exists(vnvram)) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM has not been created"); + return -EPERM; + } + + QLIST_FOREACH(entry, &vnvram->entries_head, next) { + if (!strncmp(entry->name, (char *)entry_name, sizeof(*entry_name))) { + if (entry->cur_size != 0) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, + "VNVRAM entry already written to disk"); + return -EPERM; + } + QLIST_REMOVE(entry, next); + g_free(entry); + DPRINTF("%s: Deregistered VNVRAM entry '%s'\n", __func__, + (char *)entry_name); + return 0; + } + } + + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM entry not found"); + + return -ENOENT; +} + +/* + * Read a VNVRAM blob from the specified drive entry + */ +int vnvram_read_entry(VNVRAM *vnvram, const VNVRAMEntryName *entry_name, + char **blob, uint32_t *blob_size) +{ + int rc; + VNVRAMEntry *entry; + VNVRAMRWRequest *rwr; + + *blob = NULL; + *blob_size = 0; + + if (!vnvram_exists(vnvram)) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM has not been created"); + return -EPERM; + } + + entry = vnvram_find_entry(vnvram, entry_name); + if (!entry) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM entry not found"); + return -ENOENT; + } + + rwr = vnvram_rwrequest_init_read(vnvram, entry, blob, blob_size); + + rc = vnvram_rwrequest_schedule(rwr); + + g_free(rwr); + + return rc; +} + +/* + * Write a VNVRAM blob to the specified drive entry + */ +int vnvram_write_entry(VNVRAM *vnvram, const VNVRAMEntryName *entry_name, + char *blob, uint32_t blob_size) +{ + int rc; + VNVRAMEntry *entry; + VNVRAMRWRequest *rwr; + + if (!vnvram_exists(vnvram)) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM has not been created"); + return -EPERM; + } + + entry = vnvram_find_entry(vnvram, entry_name); + if (!entry) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM entry not found"); + return -ENOENT; + } + + rwr = vnvram_rwrequest_init_write(vnvram, entry, blob, blob_size); + + rc = vnvram_rwrequest_schedule(rwr); + + g_free(rwr); + + return rc; +} + +/* + * Delete a VNVRAM from memory + */ +int vnvram_delete(VNVRAM *vnvram) +{ + VNVRAMEntry *entry; + + if (!vnvram_exists(vnvram)) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "VNVRAM has not been created"); + return -EPERM; + } + + QLIST_FOREACH(entry, &vnvram->entries_head, next) { + QLIST_REMOVE(entry, next); + g_free(entry); + } + + QLIST_REMOVE(vnvram, list); + + DPRINTF("%s: VNVRAM with drive '%s' deleted from memory\n", + __func__, vnvram->drv_id); + + g_free(vnvram->drv_id); + g_free(vnvram); + + return 0; +} diff --git a/vnvram.h b/vnvram.h index b6d7cd7..c1055b4 100644 --- a/vnvram.h +++ b/vnvram.h @@ -14,9 +14,23 @@ #ifndef _QEMU_VNVRAM_H_ #define _QEMU_VNVRAM_H_ +#include <stdint.h> +#include <stdbool.h> + typedef struct VNVRAM VNVRAM; #define VNVRAM_ENTRY_NAME_LENGTH 16 typedef char VNVRAMEntryName[VNVRAM_ENTRY_NAME_LENGTH]; +int vnvram_init(void); +VNVRAM *vnvram_create(const char *drv_id, bool fail_on_invalid, int *errcode); +int vnvram_register_entry(VNVRAM *vnvram, const VNVRAMEntryName *entry_name, + uint32_t max_size); +int vnvram_deregister_entry(VNVRAM *vnvram, const VNVRAMEntryName *entry_name); +int vnvram_read_entry(VNVRAM *vnvram, const VNVRAMEntryName *entry_name, + char **blob, uint32_t *blob_size); +int vnvram_write_entry(VNVRAM *vnvram, const VNVRAMEntryName *entry_name, + char *blob, uint32_t blob_size); +int vnvram_delete(VNVRAM *vnvram); + #endif -- 1.7.1