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" 142a9e2e59SSteve Sistare #include "file.h" 152a9e2e59SSteve Sistare #include "migration.h" 162a9e2e59SSteve Sistare #include "io/channel-file.h" 1773f6f9a1SFabiano Rosas #include "io/channel-socket.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*bd4480b0SFabiano Rosas bool ret = true; 58b7b03eb6SFabiano Rosas 59b7b03eb6SFabiano Rosas ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); 60b7b03eb6SFabiano Rosas if (!ioc) { 61*bd4480b0SFabiano Rosas ret = false; 62b7b03eb6SFabiano Rosas goto out; 63b7b03eb6SFabiano Rosas } 64b7b03eb6SFabiano Rosas 65b7b03eb6SFabiano Rosas multifd_channel_connect(opaque, QIO_CHANNEL(ioc)); 66b7b03eb6SFabiano Rosas 67b7b03eb6SFabiano Rosas out: 68b7b03eb6SFabiano Rosas /* 69b7b03eb6SFabiano Rosas * File channel creation is synchronous. However posting this 70b7b03eb6SFabiano Rosas * semaphore here is simpler than adding a special case. 71b7b03eb6SFabiano Rosas */ 72b7b03eb6SFabiano Rosas multifd_send_channel_created(); 73b7b03eb6SFabiano Rosas 74b7b03eb6SFabiano Rosas return ret; 75b7b03eb6SFabiano Rosas } 76b7b03eb6SFabiano Rosas 7702afba63SFabiano Rosas void file_start_outgoing_migration(MigrationState *s, 7802afba63SFabiano Rosas FileMigrationArgs *file_args, Error **errp) 792a9e2e59SSteve Sistare { 802a9e2e59SSteve Sistare g_autoptr(QIOChannelFile) fioc = NULL; 8102afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 8202afba63SFabiano Rosas uint64_t offset = file_args->offset; 832a9e2e59SSteve Sistare QIOChannel *ioc; 842a9e2e59SSteve Sistare 852a9e2e59SSteve Sistare trace_migration_file_outgoing(filename); 862a9e2e59SSteve Sistare 872a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 882a9e2e59SSteve Sistare 0600, errp); 892a9e2e59SSteve Sistare if (!fioc) { 902a9e2e59SSteve Sistare return; 912a9e2e59SSteve Sistare } 922a9e2e59SSteve Sistare 93b7b03eb6SFabiano Rosas outgoing_args.fname = g_strdup(filename); 94b7b03eb6SFabiano Rosas 952a9e2e59SSteve Sistare ioc = QIO_CHANNEL(fioc); 96385f510dSSteve Sistare if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { 97385f510dSSteve Sistare return; 98385f510dSSteve Sistare } 992a9e2e59SSteve Sistare qio_channel_set_name(ioc, "migration-file-outgoing"); 1002a9e2e59SSteve Sistare migration_channel_connect(s, ioc, NULL, NULL); 1012a9e2e59SSteve Sistare } 1022a9e2e59SSteve Sistare 1032a9e2e59SSteve Sistare static gboolean file_accept_incoming_migration(QIOChannel *ioc, 1042a9e2e59SSteve Sistare GIOCondition condition, 1052a9e2e59SSteve Sistare gpointer opaque) 1062a9e2e59SSteve Sistare { 1072a9e2e59SSteve Sistare migration_channel_process_incoming(ioc); 1082a9e2e59SSteve Sistare object_unref(OBJECT(ioc)); 1092a9e2e59SSteve Sistare return G_SOURCE_REMOVE; 1102a9e2e59SSteve Sistare } 1112a9e2e59SSteve Sistare 11274228c59SFabiano Rosas void file_create_incoming_channels(QIOChannel *ioc, Error **errp) 11374228c59SFabiano Rosas { 11474228c59SFabiano Rosas int i, fd, channels = 1; 11574228c59SFabiano Rosas g_autofree QIOChannel **iocs = NULL; 11674228c59SFabiano Rosas 11774228c59SFabiano Rosas if (migrate_multifd()) { 11874228c59SFabiano Rosas channels += migrate_multifd_channels(); 11974228c59SFabiano Rosas } 12074228c59SFabiano Rosas 12174228c59SFabiano Rosas iocs = g_new0(QIOChannel *, channels); 12274228c59SFabiano Rosas fd = QIO_CHANNEL_FILE(ioc)->fd; 12374228c59SFabiano Rosas iocs[0] = ioc; 12474228c59SFabiano Rosas 12574228c59SFabiano Rosas for (i = 1; i < channels; i++) { 12674228c59SFabiano Rosas QIOChannelFile *fioc = qio_channel_file_new_dupfd(fd, errp); 12774228c59SFabiano Rosas 12874228c59SFabiano Rosas if (!fioc) { 12974228c59SFabiano Rosas while (i) { 13074228c59SFabiano Rosas object_unref(iocs[--i]); 13174228c59SFabiano Rosas } 13274228c59SFabiano Rosas return; 13374228c59SFabiano Rosas } 13474228c59SFabiano Rosas 13574228c59SFabiano Rosas iocs[i] = QIO_CHANNEL(fioc); 13674228c59SFabiano Rosas } 13774228c59SFabiano Rosas 13874228c59SFabiano Rosas for (i = 0; i < channels; i++) { 13974228c59SFabiano Rosas qio_channel_set_name(iocs[i], "migration-file-incoming"); 14074228c59SFabiano Rosas qio_channel_add_watch_full(iocs[i], G_IO_IN, 14174228c59SFabiano Rosas file_accept_incoming_migration, 14274228c59SFabiano Rosas NULL, NULL, 14374228c59SFabiano Rosas g_main_context_get_thread_default()); 14474228c59SFabiano Rosas } 14574228c59SFabiano Rosas } 14674228c59SFabiano Rosas 14702afba63SFabiano Rosas void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) 1482a9e2e59SSteve Sistare { 14902afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 1502a9e2e59SSteve Sistare QIOChannelFile *fioc = NULL; 15102afba63SFabiano Rosas uint64_t offset = file_args->offset; 1522a9e2e59SSteve Sistare 1532a9e2e59SSteve Sistare trace_migration_file_incoming(filename); 1542a9e2e59SSteve Sistare 1552a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); 1562a9e2e59SSteve Sistare if (!fioc) { 1572a9e2e59SSteve Sistare return; 1582a9e2e59SSteve Sistare } 1592a9e2e59SSteve Sistare 1602dd7ee7aSFabiano Rosas if (offset && 1612dd7ee7aSFabiano Rosas qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { 16274228c59SFabiano Rosas object_unref(OBJECT(fioc)); 163385f510dSSteve Sistare return; 164385f510dSSteve Sistare } 1652dd7ee7aSFabiano Rosas 16674228c59SFabiano Rosas file_create_incoming_channels(QIO_CHANNEL(fioc), errp); 1672a9e2e59SSteve Sistare } 168f427d90bSFabiano Rosas 169f427d90bSFabiano Rosas int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, 170f427d90bSFabiano Rosas int niov, RAMBlock *block, Error **errp) 171f427d90bSFabiano Rosas { 17244fe138eSFabiano Rosas ssize_t ret = 0; 173f427d90bSFabiano Rosas int i, slice_idx, slice_num; 174f427d90bSFabiano Rosas uintptr_t base, next, offset; 175f427d90bSFabiano Rosas size_t len; 176f427d90bSFabiano Rosas 177f427d90bSFabiano Rosas slice_idx = 0; 178f427d90bSFabiano Rosas slice_num = 1; 179f427d90bSFabiano Rosas 180f427d90bSFabiano Rosas /* 181f427d90bSFabiano Rosas * If the iov array doesn't have contiguous elements, we need to 182f427d90bSFabiano Rosas * split it in slices because we only have one file offset for the 183f427d90bSFabiano Rosas * whole iov. Do this here so callers don't need to break the iov 184f427d90bSFabiano Rosas * array themselves. 185f427d90bSFabiano Rosas */ 186f427d90bSFabiano Rosas for (i = 0; i < niov; i++, slice_num++) { 187f427d90bSFabiano Rosas base = (uintptr_t) iov[i].iov_base; 188f427d90bSFabiano Rosas 189f427d90bSFabiano Rosas if (i != niov - 1) { 190f427d90bSFabiano Rosas len = iov[i].iov_len; 191f427d90bSFabiano Rosas next = (uintptr_t) iov[i + 1].iov_base; 192f427d90bSFabiano Rosas 193f427d90bSFabiano Rosas if (base + len == next) { 194f427d90bSFabiano Rosas continue; 195f427d90bSFabiano Rosas } 196f427d90bSFabiano Rosas } 197f427d90bSFabiano Rosas 198f427d90bSFabiano Rosas /* 199f427d90bSFabiano Rosas * Use the offset of the first element of the segment that 200f427d90bSFabiano Rosas * we're sending. 201f427d90bSFabiano Rosas */ 202f427d90bSFabiano Rosas offset = (uintptr_t) iov[slice_idx].iov_base - (uintptr_t) block->host; 203f427d90bSFabiano Rosas if (offset >= block->used_length) { 204a1bb5dd1SAnthony PERARD error_setg(errp, "offset %" PRIxPTR 205f427d90bSFabiano Rosas "outside of ramblock %s range", offset, block->idstr); 206f427d90bSFabiano Rosas ret = -1; 207f427d90bSFabiano Rosas break; 208f427d90bSFabiano Rosas } 209f427d90bSFabiano Rosas 210f427d90bSFabiano Rosas ret = qio_channel_pwritev(ioc, &iov[slice_idx], slice_num, 211f427d90bSFabiano Rosas block->pages_offset + offset, errp); 212f427d90bSFabiano Rosas if (ret < 0) { 213f427d90bSFabiano Rosas break; 214f427d90bSFabiano Rosas } 215f427d90bSFabiano Rosas 216f427d90bSFabiano Rosas slice_idx += slice_num; 217f427d90bSFabiano Rosas slice_num = 0; 218f427d90bSFabiano Rosas } 219f427d90bSFabiano Rosas 220f427d90bSFabiano Rosas return (ret < 0) ? ret : 0; 221f427d90bSFabiano Rosas } 222a49d15a3SFabiano Rosas 223a49d15a3SFabiano Rosas int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp) 224a49d15a3SFabiano Rosas { 225a49d15a3SFabiano Rosas MultiFDRecvData *data = p->data; 226a49d15a3SFabiano Rosas size_t ret; 227a49d15a3SFabiano Rosas 228a49d15a3SFabiano Rosas ret = qio_channel_pread(p->c, (char *) data->opaque, 229a49d15a3SFabiano Rosas data->size, data->file_offset, errp); 230a49d15a3SFabiano Rosas if (ret != data->size) { 231a49d15a3SFabiano Rosas error_prepend(errp, 232a49d15a3SFabiano Rosas "multifd recv (%u): read 0x%zx, expected 0x%zx", 233a49d15a3SFabiano Rosas p->id, ret, data->size); 234a49d15a3SFabiano Rosas return -1; 235a49d15a3SFabiano Rosas } 236a49d15a3SFabiano Rosas 237a49d15a3SFabiano Rosas return 0; 238a49d15a3SFabiano Rosas } 239