xref: /openbmc/qemu/migration/file.c (revision 66210a1a30f2384bb59f9dad8d769dba56dd30f1)
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