12a9e2e59SSteve Sistare /* 22a9e2e59SSteve Sistare * Copyright (c) 2021-2023 Oracle and/or its affiliates. 32a9e2e59SSteve Sistare * 42a9e2e59SSteve Sistare * This work is licensed under the terms of the GNU GPL, version 2 or later. 52a9e2e59SSteve Sistare * See the COPYING file in the top-level directory. 62a9e2e59SSteve Sistare */ 72a9e2e59SSteve Sistare 82a9e2e59SSteve Sistare #include "qemu/osdep.h" 9d117ed06SFabiano Rosas #include "exec/ramblock.h" 10385f510dSSteve Sistare #include "qemu/cutils.h" 112dd7ee7aSFabiano Rosas #include "qemu/error-report.h" 12385f510dSSteve Sistare #include "qapi/error.h" 132a9e2e59SSteve Sistare #include "channel.h" 14*decdc767SFabiano Rosas #include "fd.h" 152a9e2e59SSteve Sistare #include "file.h" 162a9e2e59SSteve Sistare #include "migration.h" 172a9e2e59SSteve Sistare #include "io/channel-file.h" 182a9e2e59SSteve Sistare #include "io/channel-util.h" 192dd7ee7aSFabiano Rosas #include "options.h" 202a9e2e59SSteve Sistare #include "trace.h" 212a9e2e59SSteve Sistare 22385f510dSSteve Sistare #define OFFSET_OPTION ",offset=" 23385f510dSSteve Sistare 24b7b03eb6SFabiano Rosas static struct FileOutgoingArgs { 25b7b03eb6SFabiano Rosas char *fname; 26b7b03eb6SFabiano Rosas } outgoing_args; 27b7b03eb6SFabiano Rosas 28385f510dSSteve Sistare /* Remove the offset option from @filespec and return it in @offsetp. */ 29385f510dSSteve Sistare 3072a8192eSHet Gala int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) 31385f510dSSteve Sistare { 32385f510dSSteve Sistare char *option = strstr(filespec, OFFSET_OPTION); 33385f510dSSteve Sistare int ret; 34385f510dSSteve Sistare 35385f510dSSteve Sistare if (option) { 36385f510dSSteve Sistare *option = 0; 37385f510dSSteve Sistare option += sizeof(OFFSET_OPTION) - 1; 38385f510dSSteve Sistare ret = qemu_strtosz(option, NULL, offsetp); 39385f510dSSteve Sistare if (ret) { 40385f510dSSteve Sistare error_setg_errno(errp, -ret, "file URI has bad offset %s", option); 41385f510dSSteve Sistare return -1; 42385f510dSSteve Sistare } 43385f510dSSteve Sistare } 44385f510dSSteve Sistare return 0; 45385f510dSSteve Sistare } 46385f510dSSteve Sistare 47b7b03eb6SFabiano Rosas void file_cleanup_outgoing_migration(void) 48b7b03eb6SFabiano Rosas { 49b7b03eb6SFabiano Rosas g_free(outgoing_args.fname); 50b7b03eb6SFabiano Rosas outgoing_args.fname = NULL; 51b7b03eb6SFabiano Rosas } 52b7b03eb6SFabiano Rosas 53b7b03eb6SFabiano Rosas bool file_send_channel_create(gpointer opaque, Error **errp) 54b7b03eb6SFabiano Rosas { 55b7b03eb6SFabiano Rosas QIOChannelFile *ioc; 56b7b03eb6SFabiano Rosas int flags = O_WRONLY; 57*decdc767SFabiano Rosas bool ret = false; 58*decdc767SFabiano Rosas int fd = fd_args_get_fd(); 59b7b03eb6SFabiano Rosas 60*decdc767SFabiano Rosas if (fd && fd != -1) { 61*decdc767SFabiano Rosas ioc = qio_channel_file_new_fd(dup(fd)); 62*decdc767SFabiano Rosas } else { 63b7b03eb6SFabiano Rosas ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); 64b7b03eb6SFabiano Rosas if (!ioc) { 65b7b03eb6SFabiano Rosas goto out; 66b7b03eb6SFabiano Rosas } 67*decdc767SFabiano Rosas } 68b7b03eb6SFabiano Rosas 69b7b03eb6SFabiano Rosas multifd_channel_connect(opaque, QIO_CHANNEL(ioc)); 70*decdc767SFabiano Rosas ret = true; 71b7b03eb6SFabiano Rosas 72b7b03eb6SFabiano Rosas out: 73b7b03eb6SFabiano Rosas /* 74b7b03eb6SFabiano Rosas * File channel creation is synchronous. However posting this 75b7b03eb6SFabiano Rosas * semaphore here is simpler than adding a special case. 76b7b03eb6SFabiano Rosas */ 77b7b03eb6SFabiano Rosas multifd_send_channel_created(); 78b7b03eb6SFabiano Rosas 79b7b03eb6SFabiano Rosas return ret; 80b7b03eb6SFabiano Rosas } 81b7b03eb6SFabiano Rosas 8202afba63SFabiano Rosas void file_start_outgoing_migration(MigrationState *s, 8302afba63SFabiano Rosas FileMigrationArgs *file_args, Error **errp) 842a9e2e59SSteve Sistare { 852a9e2e59SSteve Sistare g_autoptr(QIOChannelFile) fioc = NULL; 8602afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 8702afba63SFabiano Rosas uint64_t offset = file_args->offset; 882a9e2e59SSteve Sistare QIOChannel *ioc; 892a9e2e59SSteve Sistare 902a9e2e59SSteve Sistare trace_migration_file_outgoing(filename); 912a9e2e59SSteve Sistare 922a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 932a9e2e59SSteve Sistare 0600, errp); 942a9e2e59SSteve Sistare if (!fioc) { 952a9e2e59SSteve Sistare return; 962a9e2e59SSteve Sistare } 972a9e2e59SSteve Sistare 98b7b03eb6SFabiano Rosas outgoing_args.fname = g_strdup(filename); 99b7b03eb6SFabiano Rosas 1002a9e2e59SSteve Sistare ioc = QIO_CHANNEL(fioc); 101385f510dSSteve Sistare if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { 102385f510dSSteve Sistare return; 103385f510dSSteve Sistare } 1042a9e2e59SSteve Sistare qio_channel_set_name(ioc, "migration-file-outgoing"); 1052a9e2e59SSteve Sistare migration_channel_connect(s, ioc, NULL, NULL); 1062a9e2e59SSteve Sistare } 1072a9e2e59SSteve Sistare 1082a9e2e59SSteve Sistare static gboolean file_accept_incoming_migration(QIOChannel *ioc, 1092a9e2e59SSteve Sistare GIOCondition condition, 1102a9e2e59SSteve Sistare gpointer opaque) 1112a9e2e59SSteve Sistare { 1122a9e2e59SSteve Sistare migration_channel_process_incoming(ioc); 1132a9e2e59SSteve Sistare object_unref(OBJECT(ioc)); 1142a9e2e59SSteve Sistare return G_SOURCE_REMOVE; 1152a9e2e59SSteve Sistare } 1162a9e2e59SSteve Sistare 11702afba63SFabiano Rosas void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) 1182a9e2e59SSteve Sistare { 11902afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 1202a9e2e59SSteve Sistare QIOChannelFile *fioc = NULL; 12102afba63SFabiano Rosas uint64_t offset = file_args->offset; 1222dd7ee7aSFabiano Rosas int channels = 1; 1232dd7ee7aSFabiano Rosas int i = 0; 1242a9e2e59SSteve Sistare 1252a9e2e59SSteve Sistare trace_migration_file_incoming(filename); 1262a9e2e59SSteve Sistare 1272a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); 1282a9e2e59SSteve Sistare if (!fioc) { 1292a9e2e59SSteve Sistare return; 1302a9e2e59SSteve Sistare } 1312a9e2e59SSteve Sistare 1322dd7ee7aSFabiano Rosas if (offset && 1332dd7ee7aSFabiano Rosas qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { 134385f510dSSteve Sistare return; 135385f510dSSteve Sistare } 1362dd7ee7aSFabiano Rosas 1372dd7ee7aSFabiano Rosas if (migrate_multifd()) { 1382dd7ee7aSFabiano Rosas channels += migrate_multifd_channels(); 1392dd7ee7aSFabiano Rosas } 1402dd7ee7aSFabiano Rosas 1412dd7ee7aSFabiano Rosas do { 1422dd7ee7aSFabiano Rosas QIOChannel *ioc = QIO_CHANNEL(fioc); 1432dd7ee7aSFabiano Rosas 1442dd7ee7aSFabiano Rosas qio_channel_set_name(ioc, "migration-file-incoming"); 1452a9e2e59SSteve Sistare qio_channel_add_watch_full(ioc, G_IO_IN, 1462a9e2e59SSteve Sistare file_accept_incoming_migration, 1472a9e2e59SSteve Sistare NULL, NULL, 1482a9e2e59SSteve Sistare g_main_context_get_thread_default()); 1492dd7ee7aSFabiano Rosas 1502dd7ee7aSFabiano Rosas fioc = qio_channel_file_new_fd(dup(fioc->fd)); 1512dd7ee7aSFabiano Rosas 1522dd7ee7aSFabiano Rosas if (!fioc || fioc->fd == -1) { 1532dd7ee7aSFabiano Rosas error_setg(errp, "Error creating migration incoming channel"); 1542dd7ee7aSFabiano Rosas break; 1552dd7ee7aSFabiano Rosas } 1562dd7ee7aSFabiano Rosas } while (++i < channels); 1572a9e2e59SSteve Sistare } 158f427d90bSFabiano Rosas 159f427d90bSFabiano Rosas int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, 160f427d90bSFabiano Rosas int niov, RAMBlock *block, Error **errp) 161f427d90bSFabiano Rosas { 162f427d90bSFabiano Rosas ssize_t ret = -1; 163f427d90bSFabiano Rosas int i, slice_idx, slice_num; 164f427d90bSFabiano Rosas uintptr_t base, next, offset; 165f427d90bSFabiano Rosas size_t len; 166f427d90bSFabiano Rosas 167f427d90bSFabiano Rosas slice_idx = 0; 168f427d90bSFabiano Rosas slice_num = 1; 169f427d90bSFabiano Rosas 170f427d90bSFabiano Rosas /* 171f427d90bSFabiano Rosas * If the iov array doesn't have contiguous elements, we need to 172f427d90bSFabiano Rosas * split it in slices because we only have one file offset for the 173f427d90bSFabiano Rosas * whole iov. Do this here so callers don't need to break the iov 174f427d90bSFabiano Rosas * array themselves. 175f427d90bSFabiano Rosas */ 176f427d90bSFabiano Rosas for (i = 0; i < niov; i++, slice_num++) { 177f427d90bSFabiano Rosas base = (uintptr_t) iov[i].iov_base; 178f427d90bSFabiano Rosas 179f427d90bSFabiano Rosas if (i != niov - 1) { 180f427d90bSFabiano Rosas len = iov[i].iov_len; 181f427d90bSFabiano Rosas next = (uintptr_t) iov[i + 1].iov_base; 182f427d90bSFabiano Rosas 183f427d90bSFabiano Rosas if (base + len == next) { 184f427d90bSFabiano Rosas continue; 185f427d90bSFabiano Rosas } 186f427d90bSFabiano Rosas } 187f427d90bSFabiano Rosas 188f427d90bSFabiano Rosas /* 189f427d90bSFabiano Rosas * Use the offset of the first element of the segment that 190f427d90bSFabiano Rosas * we're sending. 191f427d90bSFabiano Rosas */ 192f427d90bSFabiano Rosas offset = (uintptr_t) iov[slice_idx].iov_base - (uintptr_t) block->host; 193f427d90bSFabiano Rosas if (offset >= block->used_length) { 194f427d90bSFabiano Rosas error_setg(errp, "offset " RAM_ADDR_FMT 195f427d90bSFabiano Rosas "outside of ramblock %s range", offset, block->idstr); 196f427d90bSFabiano Rosas ret = -1; 197f427d90bSFabiano Rosas break; 198f427d90bSFabiano Rosas } 199f427d90bSFabiano Rosas 200f427d90bSFabiano Rosas ret = qio_channel_pwritev(ioc, &iov[slice_idx], slice_num, 201f427d90bSFabiano Rosas block->pages_offset + offset, errp); 202f427d90bSFabiano Rosas if (ret < 0) { 203f427d90bSFabiano Rosas break; 204f427d90bSFabiano Rosas } 205f427d90bSFabiano Rosas 206f427d90bSFabiano Rosas slice_idx += slice_num; 207f427d90bSFabiano Rosas slice_num = 0; 208f427d90bSFabiano Rosas } 209f427d90bSFabiano Rosas 210f427d90bSFabiano Rosas return (ret < 0) ? ret : 0; 211f427d90bSFabiano Rosas } 212a49d15a3SFabiano Rosas 213a49d15a3SFabiano Rosas int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp) 214a49d15a3SFabiano Rosas { 215a49d15a3SFabiano Rosas MultiFDRecvData *data = p->data; 216a49d15a3SFabiano Rosas size_t ret; 217a49d15a3SFabiano Rosas 218a49d15a3SFabiano Rosas ret = qio_channel_pread(p->c, (char *) data->opaque, 219a49d15a3SFabiano Rosas data->size, data->file_offset, errp); 220a49d15a3SFabiano Rosas if (ret != data->size) { 221a49d15a3SFabiano Rosas error_prepend(errp, 222a49d15a3SFabiano Rosas "multifd recv (%u): read 0x%zx, expected 0x%zx", 223a49d15a3SFabiano Rosas p->id, ret, data->size); 224a49d15a3SFabiano Rosas return -1; 225a49d15a3SFabiano Rosas } 226a49d15a3SFabiano Rosas 227a49d15a3SFabiano Rosas return 0; 228a49d15a3SFabiano Rosas } 229