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 "fd.h" 15 #include "file.h" 16 #include "migration.h" 17 #include "io/channel-file.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 = false; 58 int fd = fd_args_get_fd(); 59 60 if (fd && fd != -1) { 61 ioc = qio_channel_file_new_fd(dup(fd)); 62 } else { 63 ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); 64 if (!ioc) { 65 goto out; 66 } 67 } 68 69 multifd_channel_connect(opaque, QIO_CHANNEL(ioc)); 70 ret = true; 71 72 out: 73 /* 74 * File channel creation is synchronous. However posting this 75 * semaphore here is simpler than adding a special case. 76 */ 77 multifd_send_channel_created(); 78 79 return ret; 80 } 81 82 void file_start_outgoing_migration(MigrationState *s, 83 FileMigrationArgs *file_args, Error **errp) 84 { 85 g_autoptr(QIOChannelFile) fioc = NULL; 86 g_autofree char *filename = g_strdup(file_args->filename); 87 uint64_t offset = file_args->offset; 88 QIOChannel *ioc; 89 90 trace_migration_file_outgoing(filename); 91 92 fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 93 0600, errp); 94 if (!fioc) { 95 return; 96 } 97 98 outgoing_args.fname = g_strdup(filename); 99 100 ioc = QIO_CHANNEL(fioc); 101 if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { 102 return; 103 } 104 qio_channel_set_name(ioc, "migration-file-outgoing"); 105 migration_channel_connect(s, ioc, NULL, NULL); 106 } 107 108 static gboolean file_accept_incoming_migration(QIOChannel *ioc, 109 GIOCondition condition, 110 gpointer opaque) 111 { 112 migration_channel_process_incoming(ioc); 113 object_unref(OBJECT(ioc)); 114 return G_SOURCE_REMOVE; 115 } 116 117 void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) 118 { 119 g_autofree char *filename = g_strdup(file_args->filename); 120 QIOChannelFile *fioc = NULL; 121 uint64_t offset = file_args->offset; 122 int channels = 1; 123 int i = 0; 124 125 trace_migration_file_incoming(filename); 126 127 fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); 128 if (!fioc) { 129 return; 130 } 131 132 if (offset && 133 qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { 134 return; 135 } 136 137 if (migrate_multifd()) { 138 channels += migrate_multifd_channels(); 139 } 140 141 do { 142 QIOChannel *ioc = QIO_CHANNEL(fioc); 143 144 qio_channel_set_name(ioc, "migration-file-incoming"); 145 qio_channel_add_watch_full(ioc, G_IO_IN, 146 file_accept_incoming_migration, 147 NULL, NULL, 148 g_main_context_get_thread_default()); 149 150 fioc = qio_channel_file_new_fd(dup(fioc->fd)); 151 152 if (!fioc || fioc->fd == -1) { 153 error_setg(errp, "Error creating migration incoming channel"); 154 break; 155 } 156 } while (++i < channels); 157 } 158 159 int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, 160 int niov, RAMBlock *block, Error **errp) 161 { 162 ssize_t ret = -1; 163 int i, slice_idx, slice_num; 164 uintptr_t base, next, offset; 165 size_t len; 166 167 slice_idx = 0; 168 slice_num = 1; 169 170 /* 171 * If the iov array doesn't have contiguous elements, we need to 172 * split it in slices because we only have one file offset for the 173 * whole iov. Do this here so callers don't need to break the iov 174 * array themselves. 175 */ 176 for (i = 0; i < niov; i++, slice_num++) { 177 base = (uintptr_t) iov[i].iov_base; 178 179 if (i != niov - 1) { 180 len = iov[i].iov_len; 181 next = (uintptr_t) iov[i + 1].iov_base; 182 183 if (base + len == next) { 184 continue; 185 } 186 } 187 188 /* 189 * Use the offset of the first element of the segment that 190 * we're sending. 191 */ 192 offset = (uintptr_t) iov[slice_idx].iov_base - (uintptr_t) block->host; 193 if (offset >= block->used_length) { 194 error_setg(errp, "offset " RAM_ADDR_FMT 195 "outside of ramblock %s range", offset, block->idstr); 196 ret = -1; 197 break; 198 } 199 200 ret = qio_channel_pwritev(ioc, &iov[slice_idx], slice_num, 201 block->pages_offset + offset, errp); 202 if (ret < 0) { 203 break; 204 } 205 206 slice_idx += slice_num; 207 slice_num = 0; 208 } 209 210 return (ret < 0) ? ret : 0; 211 } 212 213 int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp) 214 { 215 MultiFDRecvData *data = p->data; 216 size_t ret; 217 218 ret = qio_channel_pread(p->c, (char *) data->opaque, 219 data->size, data->file_offset, errp); 220 if (ret != data->size) { 221 error_prepend(errp, 222 "multifd recv (%u): read 0x%zx, expected 0x%zx", 223 p->id, ret, data->size); 224 return -1; 225 } 226 227 return 0; 228 } 229