shim_migrate.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /* Copyright (C) 2014 Stony Brook University
  2. This file is part of Graphene Library OS.
  3. Graphene Library OS is free software: you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public License
  5. as published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. Graphene Library OS is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. /*
  14. * shim_migrate.c
  15. *
  16. * Implementation of system call "checkpoint" and "restore".
  17. */
  18. #include <asm/mman.h>
  19. #include <errno.h>
  20. #include <linux/fcntl.h>
  21. #include <pal.h>
  22. #include <pal_error.h>
  23. #include <shim_checkpoint.h>
  24. #include <shim_fs.h>
  25. #include <shim_handle.h>
  26. #include <shim_internal.h>
  27. #include <shim_ipc.h>
  28. #include <shim_table.h>
  29. #include <shim_thread.h>
  30. #include <shim_vma.h>
  31. /* cp_session objects are on the cp_sessions list, by the list field */
  32. /* cp_threads are organized onto a list, handing off of the
  33. * cp_session->registered_threads list. */
  34. DEFINE_LIST(cp_thread);
  35. struct cp_thread {
  36. struct shim_thread* thread;
  37. LIST_TYPE(cp_thread) list;
  38. };
  39. DEFINE_LIST(cp_session);
  40. DEFINE_LISTP(cp_thread);
  41. struct cp_session {
  42. IDTYPE sid;
  43. struct shim_handle* cpfile;
  44. LISTP_TYPE(cp_thread) registered_threads;
  45. LIST_TYPE(cp_session) list;
  46. PAL_HANDLE finish_event;
  47. struct shim_cp_store cpstore;
  48. };
  49. DEFINE_LISTP(cp_session);
  50. LISTP_TYPE(cp_session) cp_sessions;
  51. int create_checkpoint(const char* cpdir, IDTYPE* sid) {
  52. struct cp_session* cpsession = malloc(sizeof(struct cp_session));
  53. if (!cpsession)
  54. return -ENOMEM;
  55. int ret = 0;
  56. INIT_LISTP(&cpsession->registered_threads);
  57. INIT_LIST_HEAD(cpsession, list);
  58. cpsession->finish_event = DkNotificationEventCreate(PAL_FALSE);
  59. cpsession->cpfile = NULL;
  60. int len = strlen(cpdir);
  61. char* filename = __alloca(len + 10);
  62. memcpy(filename, cpdir, len);
  63. filename[len] = '/';
  64. snprintf(filename + len + 1, 9, "%08x", cur_process.vmid);
  65. cpsession->cpfile = get_new_handle();
  66. if (!cpsession->cpfile) {
  67. ret = -ENOMEM;
  68. goto err;
  69. }
  70. /* the directory might not be created. At least try to create it */
  71. if ((ret = open_namei(NULL, NULL, cpdir, O_CREAT | O_DIRECTORY, 0700, NULL)) < 0 &&
  72. ret != -EEXIST)
  73. goto err;
  74. if ((ret = open_namei(cpsession->cpfile, NULL, filename, O_CREAT | O_EXCL | O_RDWR, 0600,
  75. NULL)) < 0)
  76. goto err;
  77. get_handle(cpsession->cpfile);
  78. MASTER_LOCK();
  79. struct cp_session* s;
  80. if (*sid) {
  81. LISTP_FOR_EACH_ENTRY(s, &cp_sessions, list) {
  82. if (s->sid == *sid) {
  83. ret = 0;
  84. goto err_locked;
  85. }
  86. }
  87. } else {
  88. retry:
  89. ret = DkRandomBitsRead(&cpsession->sid, sizeof(cpsession->sid));
  90. if (ret < 0) {
  91. ret = -convert_pal_errno(-ret);
  92. goto err_locked;
  93. }
  94. LISTP_FOR_EACH_ENTRY(s, &cp_sessions, list) {
  95. if (s->sid == cpsession->sid)
  96. goto retry;
  97. }
  98. *sid = cpsession->sid;
  99. }
  100. LISTP_ADD_TAIL(cpsession, &cp_sessions, list);
  101. MASTER_UNLOCK();
  102. return 0;
  103. err_locked:
  104. MASTER_UNLOCK();
  105. err:
  106. if (cpsession->cpfile)
  107. put_handle(cpsession->cpfile);
  108. DkObjectClose(cpsession->finish_event);
  109. free(cpsession);
  110. return ret;
  111. }
  112. static int finish_checkpoint(struct cp_session* session);
  113. static int check_thread(struct shim_thread* thread, void* arg, bool* unlocked) {
  114. __UNUSED(unlocked); // Retained for API compatibility
  115. LISTP_TYPE(cp_thread)* registered = (LISTP_TYPE(cp_thread)*)arg;
  116. struct cp_thread* t;
  117. if (!thread->in_vm || !thread->is_alive)
  118. return 0;
  119. LISTP_FOR_EACH_ENTRY(t, registered, list) {
  120. if (t->thread == thread)
  121. return 0;
  122. }
  123. return 1;
  124. }
  125. int join_checkpoint(struct shim_thread* thread, IDTYPE sid) {
  126. struct cp_session* s;
  127. struct cp_session* cpsession = NULL;
  128. struct cp_thread cpthread;
  129. int ret = 0;
  130. bool do_checkpoint = false;
  131. MASTER_LOCK();
  132. LISTP_FOR_EACH_ENTRY(s, &cp_sessions, list) {
  133. if (s->sid == sid) {
  134. cpsession = s;
  135. break;
  136. }
  137. }
  138. if (!cpsession) {
  139. MASTER_UNLOCK();
  140. return -EINVAL;
  141. }
  142. INIT_LIST_HEAD(&cpthread, list);
  143. cpthread.thread = thread;
  144. LISTP_ADD_TAIL(&cpthread, &cpsession->registered_threads, list);
  145. /* find out if there is any thread that is not registered yet */
  146. ret = walk_thread_list(&check_thread, &cpsession->registered_threads);
  147. if (ret == -ESRCH)
  148. do_checkpoint = true;
  149. PAL_HANDLE finish_event = cpsession->finish_event;
  150. MASTER_UNLOCK();
  151. if (!do_checkpoint) {
  152. debug("waiting for checkpointing\n");
  153. object_wait_with_retry(finish_event);
  154. return 0;
  155. }
  156. debug("ready for checkpointing\n");
  157. ret = finish_checkpoint(cpsession);
  158. if (ret < 0)
  159. debug("failed creating checkpoint\n");
  160. else
  161. debug("finish checkpointing, time to wake up all threads\n");
  162. DkEventSet(finish_event);
  163. return ret;
  164. }
  165. static void* file_alloc(struct shim_cp_store* store, void* addr, size_t size) {
  166. assert(store->cp_file);
  167. struct shim_mount* fs = store->cp_file->fs;
  168. if (!fs || !fs->fs_ops || !fs->fs_ops->truncate || !fs->fs_ops->mmap)
  169. return NULL;
  170. if (fs->fs_ops->truncate(store->cp_file, size) < 0)
  171. return NULL;
  172. if (fs->fs_ops->mmap(store->cp_file, &addr, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED,
  173. 0) < 0)
  174. return NULL;
  175. return addr;
  176. }
  177. static int finish_checkpoint(struct cp_session* cpsession) {
  178. struct shim_cp_store* cpstore = &cpsession->cpstore;
  179. int ret;
  180. cpstore->alloc = file_alloc;
  181. BEGIN_MIGRATION_DEF(checkpoint) {
  182. DEFINE_MIGRATE(process, &cur_process, sizeof(struct shim_process));
  183. DEFINE_MIGRATE(all_mounts, NULL, 0);
  184. DEFINE_MIGRATE(all_vmas, NULL, 0);
  185. DEFINE_MIGRATE(all_running_threads, NULL, 0);
  186. DEFINE_MIGRATE(brk, NULL, 0);
  187. DEFINE_MIGRATE(loaded_libraries, NULL, 0);
  188. #ifdef DEBUG
  189. DEFINE_MIGRATE(gdb_map, NULL, 0);
  190. #endif
  191. DEFINE_MIGRATE(migratable, NULL, 0);
  192. }
  193. END_MIGRATION_DEF(checkpoint)
  194. if ((ret = START_MIGRATE(cpstore, checkpoint)) < 0)
  195. return ret;
  196. struct cp_header* hdr = (struct cp_header*)cpstore->base;
  197. hdr->addr = (void*)cpstore->base;
  198. hdr->size = cpstore->offset;
  199. DkStreamUnmap((void*)cpstore->base, cpstore->bound);
  200. put_handle(cpstore->cp_file);
  201. return 0;
  202. }
  203. int shim_do_checkpoint(const char* filename) {
  204. IDTYPE session = 0;
  205. int ret = 0;
  206. ret = shim_do_mkdir(filename, 0700);
  207. if (ret < 0)
  208. return ret;
  209. shim_tcb_t* tcb = shim_get_tls();
  210. assert(tcb && tcb->tp);
  211. struct shim_signal signal;
  212. __store_context(tcb, NULL, &signal);
  213. ret = create_checkpoint(filename, &session);
  214. if (ret < 0) {
  215. shim_do_rmdir(filename);
  216. return ret;
  217. }
  218. ipc_checkpoint_send(filename, session);
  219. kill_all_threads(tcb->tp, session, SIGCP);
  220. ret = join_checkpoint(tcb->tp, session);
  221. if (ret < 0) {
  222. shim_do_rmdir(filename);
  223. return ret;
  224. }
  225. return 0;
  226. }