shim_migrate.c 7.4 KB

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