5
0
mirror of git://git.proxmox.com/git/proxmox-backup-qemu.git synced 2025-02-25 01:57:34 +03:00

add state serializing and loading functions

For dirty-bitmap migration, QEMU also needs to move the static state of
the library to the target. proxmox_{import,export}_state provide a means
of accessing said data in a serialized fashion.

QEMU treats the state as some unknown quantity of bytes and the result
does not need to be human-readable, so we encode it with 'bincode',
which is based on serde.

Since the quantity is only known *after* serialization, we have to
allocate the buffer ourselves. This is handled by Box::leak-ing a Rust
allocated buffer and cleaning up via the explicit
proxmox_free_state_buf function.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
This commit is contained in:
Stefan Reiter 2020-10-22 17:34:19 +02:00 committed by Thomas Lamprecht
parent 58437c69e4
commit c28f578e09
4 changed files with 74 additions and 0 deletions

View File

@ -32,3 +32,4 @@ proxmox-backup = { git = "git://git.proxmox.com/git/proxmox-backup.git", tag = "
#proxmox-backup = { path = "../proxmox-backup" }
serde_json = "1.0"
tokio = { version = "0.2.9", features = [ "blocking", "fs", "io-util", "macros", "rt-threaded", "signal", "stream", "tcp", "time", "uds" ] }
bincode = "1.0"

View File

@ -263,6 +263,26 @@ void proxmox_backup_write_data_async(ProxmoxBackupHandle *handle,
int *result,
char **error);
/**
* Serialize all state data into a byte buffer. Can be loaded again with
* proxmox_import_state. Use for migration for example.
*
* Length of the returned buffer is written to buf_size. Returned buffer must
* be freed with proxmox_free_state_buf.
*/
uint8_t *proxmox_export_state(uintptr_t *buf_size);
/**
* Free a buffer acquired from proxmox_export_state.
*/
void proxmox_free_state_buf(uint8_t *buf);
/**
* Load state serialized by proxmox_export_state. If loading fails, a message
* will be logged to stderr, but the function will not fail.
*/
void proxmox_import_state(const uint8_t *buf, uintptr_t buf_size);
/**
* Open connection to the backup server (sync)
*

View File

@ -16,6 +16,9 @@ use crate::upload_queue::*;
use lazy_static::lazy_static;
lazy_static!{
// Note: Any state stored here that needs to be sent along with migration
// needs to be specified in (de)serialize_state as well!
static ref PREVIOUS_CSUMS: Mutex<HashMap<String, [u8;32]>> = {
Mutex::new(HashMap::new())
};
@ -35,6 +38,22 @@ pub struct ImageUploadInfo {
}
pub(crate) fn serialize_state() -> Vec<u8> {
let prev_csums = &*PREVIOUS_CSUMS.lock().unwrap();
let prev_crypt_digest = &*PREVIOUS_CRYPT_CONFIG_DIGEST.lock().unwrap();
bincode::serialize(&(prev_csums, prev_crypt_digest)).unwrap()
}
pub(crate) fn deserialize_state(data: &[u8]) -> Result<(), Error> {
let (prev_csums, prev_crypt_digest) = bincode::deserialize(data)?;
let mut prev_csums_guard = PREVIOUS_CSUMS.lock().unwrap();
let mut prev_crypt_digest_guard = PREVIOUS_CRYPT_CONFIG_DIGEST.lock().unwrap();
*prev_csums_guard = prev_csums;
*prev_crypt_digest_guard = prev_crypt_digest;
Ok(())
}
// Note: We alway register/upload a chunk containing zeros
async fn register_zero_chunk(
client: Arc<BackupWriter>,

View File

@ -967,3 +967,37 @@ pub extern "C" fn proxmox_restore_read_image_at_async(
callback_info.send_result(result);
});
}
/// Serialize all state data into a byte buffer. Can be loaded again with
/// proxmox_import_state. Use for migration for example.
///
/// Length of the returned buffer is written to buf_size. Returned buffer must
/// be freed with proxmox_free_state_buf.
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn proxmox_export_state(buf_size: *mut usize) -> *mut u8 {
let data = commands::serialize_state().into_boxed_slice();
unsafe { *buf_size = data.len(); }
Box::leak(data).as_mut_ptr()
}
/// Load state serialized by proxmox_export_state. If loading fails, a message
/// will be logged to stderr, but the function will not fail.
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn proxmox_import_state(buf: *const u8, buf_size: usize) {
let data = unsafe { std::slice::from_raw_parts(buf, buf_size) };
// ignore errors, just log what happened
if let Err(err) = commands::deserialize_state(data) {
eprintln!("error deserializing PBS state - {}", err);
}
}
/// Free a buffer acquired from proxmox_export_state.
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn proxmox_free_state_buf(buf: *mut u8) {
if !buf.is_null() {
unsafe { Box::from_raw(buf); }
}
}