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" 14decdc767SFabiano 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; 57decdc767SFabiano Rosas bool ret = false; 58decdc767SFabiano Rosas int fd = fd_args_get_fd(); 59b7b03eb6SFabiano Rosas 60decdc767SFabiano Rosas if (fd && fd != -1) { 61*c827fafcSFabiano Rosas ioc = qio_channel_file_new_dupfd(fd, errp); 62decdc767SFabiano Rosas } else { 63b7b03eb6SFabiano Rosas ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); 64*c827fafcSFabiano Rosas } 65*c827fafcSFabiano Rosas 66b7b03eb6SFabiano Rosas if (!ioc) { 67b7b03eb6SFabiano Rosas goto out; 68b7b03eb6SFabiano Rosas } 69b7b03eb6SFabiano Rosas 70b7b03eb6SFabiano Rosas multifd_channel_connect(opaque, QIO_CHANNEL(ioc)); 71decdc767SFabiano Rosas ret = true; 72b7b03eb6SFabiano Rosas 73b7b03eb6SFabiano Rosas out: 74b7b03eb6SFabiano Rosas /* 75b7b03eb6SFabiano Rosas * File channel creation is synchronous. However posting this 76b7b03eb6SFabiano Rosas * semaphore here is simpler than adding a special case. 77b7b03eb6SFabiano Rosas */ 78b7b03eb6SFabiano Rosas multifd_send_channel_created(); 79b7b03eb6SFabiano Rosas 80b7b03eb6SFabiano Rosas return ret; 81b7b03eb6SFabiano Rosas } 82b7b03eb6SFabiano Rosas 8302afba63SFabiano Rosas void file_start_outgoing_migration(MigrationState *s, 8402afba63SFabiano Rosas FileMigrationArgs *file_args, Error **errp) 852a9e2e59SSteve Sistare { 862a9e2e59SSteve Sistare g_autoptr(QIOChannelFile) fioc = NULL; 8702afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 8802afba63SFabiano Rosas uint64_t offset = file_args->offset; 892a9e2e59SSteve Sistare QIOChannel *ioc; 902a9e2e59SSteve Sistare 912a9e2e59SSteve Sistare trace_migration_file_outgoing(filename); 922a9e2e59SSteve Sistare 932a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 942a9e2e59SSteve Sistare 0600, errp); 952a9e2e59SSteve Sistare if (!fioc) { 962a9e2e59SSteve Sistare return; 972a9e2e59SSteve Sistare } 982a9e2e59SSteve Sistare 99b7b03eb6SFabiano Rosas outgoing_args.fname = g_strdup(filename); 100b7b03eb6SFabiano Rosas 1012a9e2e59SSteve Sistare ioc = QIO_CHANNEL(fioc); 102385f510dSSteve Sistare if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { 103385f510dSSteve Sistare return; 104385f510dSSteve Sistare } 1052a9e2e59SSteve Sistare qio_channel_set_name(ioc, "migration-file-outgoing"); 1062a9e2e59SSteve Sistare migration_channel_connect(s, ioc, NULL, NULL); 1072a9e2e59SSteve Sistare } 1082a9e2e59SSteve Sistare 1092a9e2e59SSteve Sistare static gboolean file_accept_incoming_migration(QIOChannel *ioc, 1102a9e2e59SSteve Sistare GIOCondition condition, 1112a9e2e59SSteve Sistare gpointer opaque) 1122a9e2e59SSteve Sistare { 1132a9e2e59SSteve Sistare migration_channel_process_incoming(ioc); 1142a9e2e59SSteve Sistare object_unref(OBJECT(ioc)); 1152a9e2e59SSteve Sistare return G_SOURCE_REMOVE; 1162a9e2e59SSteve Sistare } 1172a9e2e59SSteve Sistare 11802afba63SFabiano Rosas void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) 1192a9e2e59SSteve Sistare { 12002afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 1212a9e2e59SSteve Sistare QIOChannelFile *fioc = NULL; 12202afba63SFabiano Rosas uint64_t offset = file_args->offset; 1232dd7ee7aSFabiano Rosas int channels = 1; 1242dd7ee7aSFabiano Rosas int i = 0; 1252a9e2e59SSteve Sistare 1262a9e2e59SSteve Sistare trace_migration_file_incoming(filename); 1272a9e2e59SSteve Sistare 1282a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); 1292a9e2e59SSteve Sistare if (!fioc) { 1302a9e2e59SSteve Sistare return; 1312a9e2e59SSteve Sistare } 1322a9e2e59SSteve Sistare 1332dd7ee7aSFabiano Rosas if (offset && 1342dd7ee7aSFabiano Rosas qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { 135385f510dSSteve Sistare return; 136385f510dSSteve Sistare } 1372dd7ee7aSFabiano Rosas 1382dd7ee7aSFabiano Rosas if (migrate_multifd()) { 1392dd7ee7aSFabiano Rosas channels += migrate_multifd_channels(); 1402dd7ee7aSFabiano Rosas } 1412dd7ee7aSFabiano Rosas 1422dd7ee7aSFabiano Rosas do { 1432dd7ee7aSFabiano Rosas QIOChannel *ioc = QIO_CHANNEL(fioc); 1442dd7ee7aSFabiano Rosas 1452dd7ee7aSFabiano Rosas qio_channel_set_name(ioc, "migration-file-incoming"); 1462a9e2e59SSteve Sistare qio_channel_add_watch_full(ioc, G_IO_IN, 1472a9e2e59SSteve Sistare file_accept_incoming_migration, 1482a9e2e59SSteve Sistare NULL, NULL, 1492a9e2e59SSteve Sistare g_main_context_get_thread_default()); 1502dd7ee7aSFabiano Rosas 151*c827fafcSFabiano Rosas fioc = qio_channel_file_new_dupfd(fioc->fd, errp); 1522dd7ee7aSFabiano Rosas 153*c827fafcSFabiano Rosas if (!fioc) { 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 { 16244fe138eSFabiano Rosas ssize_t ret = 0; 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) { 194a1bb5dd1SAnthony PERARD error_setg(errp, "offset %" PRIxPTR 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