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) { 61c827fafcSFabiano 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); 64c827fafcSFabiano Rosas } 65c827fafcSFabiano 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 118*74228c59SFabiano Rosas void file_create_incoming_channels(QIOChannel *ioc, Error **errp) 119*74228c59SFabiano Rosas { 120*74228c59SFabiano Rosas int i, fd, channels = 1; 121*74228c59SFabiano Rosas g_autofree QIOChannel **iocs = NULL; 122*74228c59SFabiano Rosas 123*74228c59SFabiano Rosas if (migrate_multifd()) { 124*74228c59SFabiano Rosas channels += migrate_multifd_channels(); 125*74228c59SFabiano Rosas } 126*74228c59SFabiano Rosas 127*74228c59SFabiano Rosas iocs = g_new0(QIOChannel *, channels); 128*74228c59SFabiano Rosas fd = QIO_CHANNEL_FILE(ioc)->fd; 129*74228c59SFabiano Rosas iocs[0] = ioc; 130*74228c59SFabiano Rosas 131*74228c59SFabiano Rosas for (i = 1; i < channels; i++) { 132*74228c59SFabiano Rosas QIOChannelFile *fioc = qio_channel_file_new_dupfd(fd, errp); 133*74228c59SFabiano Rosas 134*74228c59SFabiano Rosas if (!fioc) { 135*74228c59SFabiano Rosas while (i) { 136*74228c59SFabiano Rosas object_unref(iocs[--i]); 137*74228c59SFabiano Rosas } 138*74228c59SFabiano Rosas return; 139*74228c59SFabiano Rosas } 140*74228c59SFabiano Rosas 141*74228c59SFabiano Rosas iocs[i] = QIO_CHANNEL(fioc); 142*74228c59SFabiano Rosas } 143*74228c59SFabiano Rosas 144*74228c59SFabiano Rosas for (i = 0; i < channels; i++) { 145*74228c59SFabiano Rosas qio_channel_set_name(iocs[i], "migration-file-incoming"); 146*74228c59SFabiano Rosas qio_channel_add_watch_full(iocs[i], G_IO_IN, 147*74228c59SFabiano Rosas file_accept_incoming_migration, 148*74228c59SFabiano Rosas NULL, NULL, 149*74228c59SFabiano Rosas g_main_context_get_thread_default()); 150*74228c59SFabiano Rosas } 151*74228c59SFabiano Rosas } 152*74228c59SFabiano Rosas 15302afba63SFabiano Rosas void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) 1542a9e2e59SSteve Sistare { 15502afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 1562a9e2e59SSteve Sistare QIOChannelFile *fioc = NULL; 15702afba63SFabiano Rosas uint64_t offset = file_args->offset; 1582a9e2e59SSteve Sistare 1592a9e2e59SSteve Sistare trace_migration_file_incoming(filename); 1602a9e2e59SSteve Sistare 1612a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); 1622a9e2e59SSteve Sistare if (!fioc) { 1632a9e2e59SSteve Sistare return; 1642a9e2e59SSteve Sistare } 1652a9e2e59SSteve Sistare 1662dd7ee7aSFabiano Rosas if (offset && 1672dd7ee7aSFabiano Rosas qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { 168*74228c59SFabiano Rosas object_unref(OBJECT(fioc)); 169385f510dSSteve Sistare return; 170385f510dSSteve Sistare } 1712dd7ee7aSFabiano Rosas 172*74228c59SFabiano Rosas file_create_incoming_channels(QIO_CHANNEL(fioc), errp); 1732a9e2e59SSteve Sistare } 174f427d90bSFabiano Rosas 175f427d90bSFabiano Rosas int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, 176f427d90bSFabiano Rosas int niov, RAMBlock *block, Error **errp) 177f427d90bSFabiano Rosas { 17844fe138eSFabiano Rosas ssize_t ret = 0; 179f427d90bSFabiano Rosas int i, slice_idx, slice_num; 180f427d90bSFabiano Rosas uintptr_t base, next, offset; 181f427d90bSFabiano Rosas size_t len; 182f427d90bSFabiano Rosas 183f427d90bSFabiano Rosas slice_idx = 0; 184f427d90bSFabiano Rosas slice_num = 1; 185f427d90bSFabiano Rosas 186f427d90bSFabiano Rosas /* 187f427d90bSFabiano Rosas * If the iov array doesn't have contiguous elements, we need to 188f427d90bSFabiano Rosas * split it in slices because we only have one file offset for the 189f427d90bSFabiano Rosas * whole iov. Do this here so callers don't need to break the iov 190f427d90bSFabiano Rosas * array themselves. 191f427d90bSFabiano Rosas */ 192f427d90bSFabiano Rosas for (i = 0; i < niov; i++, slice_num++) { 193f427d90bSFabiano Rosas base = (uintptr_t) iov[i].iov_base; 194f427d90bSFabiano Rosas 195f427d90bSFabiano Rosas if (i != niov - 1) { 196f427d90bSFabiano Rosas len = iov[i].iov_len; 197f427d90bSFabiano Rosas next = (uintptr_t) iov[i + 1].iov_base; 198f427d90bSFabiano Rosas 199f427d90bSFabiano Rosas if (base + len == next) { 200f427d90bSFabiano Rosas continue; 201f427d90bSFabiano Rosas } 202f427d90bSFabiano Rosas } 203f427d90bSFabiano Rosas 204f427d90bSFabiano Rosas /* 205f427d90bSFabiano Rosas * Use the offset of the first element of the segment that 206f427d90bSFabiano Rosas * we're sending. 207f427d90bSFabiano Rosas */ 208f427d90bSFabiano Rosas offset = (uintptr_t) iov[slice_idx].iov_base - (uintptr_t) block->host; 209f427d90bSFabiano Rosas if (offset >= block->used_length) { 210a1bb5dd1SAnthony PERARD error_setg(errp, "offset %" PRIxPTR 211f427d90bSFabiano Rosas "outside of ramblock %s range", offset, block->idstr); 212f427d90bSFabiano Rosas ret = -1; 213f427d90bSFabiano Rosas break; 214f427d90bSFabiano Rosas } 215f427d90bSFabiano Rosas 216f427d90bSFabiano Rosas ret = qio_channel_pwritev(ioc, &iov[slice_idx], slice_num, 217f427d90bSFabiano Rosas block->pages_offset + offset, errp); 218f427d90bSFabiano Rosas if (ret < 0) { 219f427d90bSFabiano Rosas break; 220f427d90bSFabiano Rosas } 221f427d90bSFabiano Rosas 222f427d90bSFabiano Rosas slice_idx += slice_num; 223f427d90bSFabiano Rosas slice_num = 0; 224f427d90bSFabiano Rosas } 225f427d90bSFabiano Rosas 226f427d90bSFabiano Rosas return (ret < 0) ? ret : 0; 227f427d90bSFabiano Rosas } 228a49d15a3SFabiano Rosas 229a49d15a3SFabiano Rosas int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp) 230a49d15a3SFabiano Rosas { 231a49d15a3SFabiano Rosas MultiFDRecvData *data = p->data; 232a49d15a3SFabiano Rosas size_t ret; 233a49d15a3SFabiano Rosas 234a49d15a3SFabiano Rosas ret = qio_channel_pread(p->c, (char *) data->opaque, 235a49d15a3SFabiano Rosas data->size, data->file_offset, errp); 236a49d15a3SFabiano Rosas if (ret != data->size) { 237a49d15a3SFabiano Rosas error_prepend(errp, 238a49d15a3SFabiano Rosas "multifd recv (%u): read 0x%zx, expected 0x%zx", 239a49d15a3SFabiano Rosas p->id, ret, data->size); 240a49d15a3SFabiano Rosas return -1; 241a49d15a3SFabiano Rosas } 242a49d15a3SFabiano Rosas 243a49d15a3SFabiano Rosas return 0; 244a49d15a3SFabiano Rosas } 245