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_dupfd(fd, errp); 62 } else { 63 ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); 64 } 65 66 if (!ioc) { 67 goto out; 68 } 69 70 multifd_channel_connect(opaque, QIO_CHANNEL(ioc)); 71 ret = true; 72 73 out: 74 /* 75 * File channel creation is synchronous. However posting this 76 * semaphore here is simpler than adding a special case. 77 */ 78 multifd_send_channel_created(); 79 80 return ret; 81 } 82 83 void file_start_outgoing_migration(MigrationState *s, 84 FileMigrationArgs *file_args, Error **errp) 85 { 86 g_autoptr(QIOChannelFile) fioc = NULL; 87 g_autofree char *filename = g_strdup(file_args->filename); 88 uint64_t offset = file_args->offset; 89 QIOChannel *ioc; 90 91 trace_migration_file_outgoing(filename); 92 93 fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 94 0600, errp); 95 if (!fioc) { 96 return; 97 } 98 99 outgoing_args.fname = g_strdup(filename); 100 101 ioc = QIO_CHANNEL(fioc); 102 if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { 103 return; 104 } 105 qio_channel_set_name(ioc, "migration-file-outgoing"); 106 migration_channel_connect(s, ioc, NULL, NULL); 107 } 108 109 static gboolean file_accept_incoming_migration(QIOChannel *ioc, 110 GIOCondition condition, 111 gpointer opaque) 112 { 113 migration_channel_process_incoming(ioc); 114 object_unref(OBJECT(ioc)); 115 return G_SOURCE_REMOVE; 116 } 117 118 void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) 119 { 120 g_autofree char *filename = g_strdup(file_args->filename); 121 QIOChannelFile *fioc = NULL; 122 uint64_t offset = file_args->offset; 123 int channels = 1; 124 int i = 0; 125 126 trace_migration_file_incoming(filename); 127 128 fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); 129 if (!fioc) { 130 return; 131 } 132 133 if (offset && 134 qio_channel_io_seek(QIO_CHANNEL(fioc), offset, SEEK_SET, errp) < 0) { 135 return; 136 } 137 138 if (migrate_multifd()) { 139 channels += migrate_multifd_channels(); 140 } 141 142 do { 143 QIOChannel *ioc = QIO_CHANNEL(fioc); 144 145 qio_channel_set_name(ioc, "migration-file-incoming"); 146 qio_channel_add_watch_full(ioc, G_IO_IN, 147 file_accept_incoming_migration, 148 NULL, NULL, 149 g_main_context_get_thread_default()); 150 151 fioc = qio_channel_file_new_dupfd(fioc->fd, errp); 152 153 if (!fioc) { 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 = 0; 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 %" PRIxPTR 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