1 /* 2 * Copyright (c) 2021-2023 Oracle and/or its affiliates. 3 * 4 * This work is licensed under the terms of the GNU GPL, version 2 or later. 5 * See the COPYING file in the top-level directory. 6 */ 7 8 #include "qemu/osdep.h" 9 #include "exec/ramblock.h" 10 #include "qemu/cutils.h" 11 #include "qemu/error-report.h" 12 #include "qapi/error.h" 13 #include "channel.h" 14 #include "file.h" 15 #include "migration.h" 16 #include "io/channel-file.h" 17 #include "io/channel-socket.h" 18 #include "io/channel-util.h" 19 #include "options.h" 20 #include "trace.h" 21 22 #define OFFSET_OPTION ",offset=" 23 24 static struct FileOutgoingArgs { 25 char *fname; 26 } outgoing_args; 27 28 /* Remove the offset option from @filespec and return it in @offsetp. */ 29 30 int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) 31 { 32 char *option = strstr(filespec, OFFSET_OPTION); 33 int ret; 34 35 if (option) { 36 *option = 0; 37 option += sizeof(OFFSET_OPTION) - 1; 38 ret = qemu_strtosz(option, NULL, offsetp); 39 if (ret) { 40 error_setg_errno(errp, -ret, "file URI has bad offset %s", option); 41 return -1; 42 } 43 } 44 return 0; 45 } 46 47 void file_cleanup_outgoing_migration(void) 48 { 49 g_free(outgoing_args.fname); 50 outgoing_args.fname = NULL; 51 } 52 53 bool file_send_channel_create(gpointer opaque, Error **errp) 54 { 55 QIOChannelFile *ioc; 56 int flags = O_WRONLY; 57 bool ret = true; 58 59 ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); 60 if (!ioc) { 61 ret = false; 62 goto out; 63 } 64 65 multifd_channel_connect(opaque, QIO_CHANNEL(ioc)); 66 67 out: 68 /* 69 * File channel creation is synchronous. However posting this 70 * semaphore here is simpler than adding a special case. 71 */ 72 multifd_send_channel_created(); 73 74 return ret; 75 } 76 77 void file_start_outgoing_migration(MigrationState *s, 78 FileMigrationArgs *file_args, Error **errp) 79 { 80 g_autoptr(QIOChannelFile) fioc = NULL; 81 g_autofree char *filename = g_strdup(file_args->filename); 82 uint64_t offset = file_args->offset; 83 QIOChannel *ioc; 84 85 trace_migration_file_outgoing(filename); 86 87 fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 88 0600, errp); 89 if (!fioc) { 90 return; 91 } 92 93 outgoing_args.fname = g_strdup(filename); 94 95 ioc = QIO_CHANNEL(fioc); 96 if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { 97 return; 98 } 99 qio_channel_set_name(ioc, "migration-file-outgoing"); 100 migration_channel_connect(s, ioc, NULL, NULL); 101 } 102 103 static gboolean file_accept_incoming_migration(QIOChannel *ioc, 104 GIOCondition condition, 105 gpointer opaque) 106 { 107 migration_channel_process_incoming(ioc); 108 object_unref(OBJECT(ioc)); 109 return G_SOURCE_REMOVE; 110 } 111 112 void file_create_incoming_channels(QIOChannel *ioc, Error **errp) 113 { 114 int i, fd, channels = 1; 115 g_autofree QIOChannel **iocs = NULL; 116 117 if (migrate_multifd()) { 118 channels += migrate_multifd_channels(); 119 } 120 121 iocs = g_new0(QIOChannel *, channels); 122 fd = QIO_CHANNEL_FILE(ioc)->fd; 123 iocs[0] = ioc; 124 125 for (i = 1; i < channels; i++) { 126 QIOChannelFile *fioc = qio_channel_file_new_dupfd(fd, errp); 127 128 if (!fioc) { 129 while (i) { 130 object_unref(iocs[--i]); 131 } 132 return; 133 } 134 135 iocs[i] = QIO_CHANNEL(fioc); 136 } 137 138 for (i = 0; i < channels; i++) { 139 qio_channel_set_name(iocs[i], "migration-file-incoming"); 140 qio_channel_add_watch_full(iocs[i], G_IO_IN, 141 file_accept_incoming_migration, 142 NULL, NULL, 143 g_main_context_get_thread_default()); 144 } 145 } 146 147 void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) 148 { 149 g_autofree char *filename = g_strdup(file_args->filename); 150 QIOChannelFile *fioc = NULL; 151 uint64_t offset = file_args->offset; 152 153 trace_migration_file_incoming(filename); 154 155 fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); 156 if (!fioc) { 157 return; 158 } 159 160 if (offset && 161 qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { 162 object_unref(OBJECT(fioc)); 163 return; 164 } 165 166 file_create_incoming_channels(QIO_CHANNEL(fioc), errp); 167 } 168 169 int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, 170 int niov, RAMBlock *block, Error **errp) 171 { 172 ssize_t ret = 0; 173 int i, slice_idx, slice_num; 174 uintptr_t base, next, offset; 175 size_t len; 176 177 slice_idx = 0; 178 slice_num = 1; 179 180 /* 181 * If the iov array doesn't have contiguous elements, we need to 182 * split it in slices because we only have one file offset for the 183 * whole iov. Do this here so callers don't need to break the iov 184 * array themselves. 185 */ 186 for (i = 0; i < niov; i++, slice_num++) { 187 base = (uintptr_t) iov[i].iov_base; 188 189 if (i != niov - 1) { 190 len = iov[i].iov_len; 191 next = (uintptr_t) iov[i + 1].iov_base; 192 193 if (base + len == next) { 194 continue; 195 } 196 } 197 198 /* 199 * Use the offset of the first element of the segment that 200 * we're sending. 201 */ 202 offset = (uintptr_t) iov[slice_idx].iov_base - (uintptr_t) block->host; 203 if (offset >= block->used_length) { 204 error_setg(errp, "offset %" PRIxPTR 205 "outside of ramblock %s range", offset, block->idstr); 206 ret = -1; 207 break; 208 } 209 210 ret = qio_channel_pwritev(ioc, &iov[slice_idx], slice_num, 211 block->pages_offset + offset, errp); 212 if (ret < 0) { 213 break; 214 } 215 216 slice_idx += slice_num; 217 slice_num = 0; 218 } 219 220 return (ret < 0) ? ret : 0; 221 } 222 223 int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp) 224 { 225 MultiFDRecvData *data = p->data; 226 size_t ret; 227 228 ret = qio_channel_pread(p->c, (char *) data->opaque, 229 data->size, data->file_offset, errp); 230 if (ret != data->size) { 231 error_prepend(errp, 232 "multifd recv (%u): read 0x%zx, expected 0x%zx", 233 p->id, ret, data->size); 234 return -1; 235 } 236 237 return 0; 238 } 239