shim_migrate.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 <pal.h>
  28. #include <pal_error.h>
  29. #include <errno.h>
  30. #include <fcntl.h>
  31. #include <asm/mman.h>
  32. #define malloc_method(size) malloc_method_file(size)
  33. #include <shim_checkpoint.h>
  34. LIST_HEAD(created_sessions);
  35. struct cpsession {
  36. IDTYPE session;
  37. struct shim_handle * cpfile;
  38. struct list_head registered_threads;
  39. struct list_head list;
  40. PAL_HANDLE finish_event;
  41. };
  42. struct cpthread {
  43. struct shim_thread * thread;
  44. struct list_head list;
  45. };
  46. static struct cpsession * current_cpsession = NULL;
  47. int create_checkpoint (const char * cpdir, IDTYPE * session)
  48. {
  49. struct cpsession * cpsession = malloc(sizeof(struct cpsession));
  50. if (!cpsession)
  51. return -ENOMEM;
  52. int ret = 0;
  53. INIT_LIST_HEAD(&cpsession->registered_threads);
  54. INIT_LIST_HEAD(&cpsession->list);
  55. cpsession->finish_event = DkNotificationEventCreate(0);
  56. cpsession->cpfile = NULL;
  57. int len = strlen(cpdir);
  58. char * filename = __alloca(len + 10);
  59. memcpy(filename, cpdir, len);
  60. filename[len] = '/';
  61. snprintf(filename + len + 1, 9, "%08x", cur_process.vmid);
  62. cpsession->cpfile = get_new_handle();
  63. if (!cpsession->cpfile) {
  64. ret = -ENOMEM;
  65. goto err;
  66. }
  67. /* the directory might not be created. At least try to create it */
  68. if ((ret = open_namei(NULL, NULL, cpdir, O_CREAT|O_DIRECTORY, 0700,
  69. NULL)) < 0
  70. && ret != -EEXIST)
  71. goto err;
  72. if ((ret = open_namei(cpsession->cpfile, NULL, filename,
  73. O_CREAT|O_EXCL|O_RDWR, 0600, NULL)) < 0)
  74. goto err;
  75. open_handle(cpsession->cpfile);
  76. master_lock();
  77. if (*session) {
  78. struct cpsession * cps;
  79. list_for_each_entry(cps, &created_sessions, list)
  80. if (cps->session == *session) {
  81. ret = 0;
  82. goto err_locked;
  83. }
  84. } else {
  85. struct cpsession * cps;
  86. retry:
  87. getrand(session, sizeof(IDTYPE));
  88. list_for_each_entry(cps, &created_sessions, list)
  89. if (cps->session == *session)
  90. goto retry;
  91. }
  92. list_add_tail(&cpsession->list, &created_sessions);
  93. if (!current_cpsession)
  94. current_cpsession = cpsession;
  95. master_unlock();
  96. return 0;
  97. err_locked:
  98. master_unlock();
  99. err:
  100. if (cpsession->cpfile)
  101. close_handle(cpsession->cpfile);
  102. DkObjectClose(cpsession->finish_event);
  103. free(cpsession);
  104. return ret;
  105. }
  106. static int finish_checkpoint (void);
  107. static int check_thread (struct shim_thread * thread, void * arg,
  108. bool * unlocked)
  109. {
  110. struct list_head * registered = (struct list_head *) arg;
  111. struct cpthread * cpt;
  112. if (!thread->in_vm || !thread->is_alive)
  113. return 0;
  114. list_for_each_entry(cpt, registered, list)
  115. if (cpt->thread == thread)
  116. return 0;
  117. return 1;
  118. }
  119. int join_checkpoint (struct shim_thread * cur, ucontext_t * context)
  120. {
  121. struct cpthread cpt;
  122. int ret = 0;
  123. bool do_checkpoint = false;
  124. master_lock();
  125. if (!current_cpsession) {
  126. master_unlock();
  127. return -EINVAL;
  128. }
  129. INIT_LIST_HEAD(&cpt.list);
  130. cpt.thread = cur;
  131. list_add_tail(&cpt.list, &current_cpsession->registered_threads);
  132. /* find out if there is any thread that is not registered yet */
  133. ret = walk_thread_list(&check_thread,
  134. &current_cpsession->registered_threads,
  135. false);
  136. if (ret == -ESRCH)
  137. do_checkpoint = true;
  138. PAL_HANDLE finish_event = current_cpsession->finish_event;
  139. master_unlock();
  140. if (!do_checkpoint) {
  141. debug("waiting for checkpointing\n");
  142. DkObjectsWaitAny(1, &finish_event, NO_TIMEOUT);
  143. return 0;
  144. }
  145. debug("ready for checkpointing\n");
  146. ret = finish_checkpoint();
  147. if (ret < 0)
  148. debug("failed creating checkpoint\n");
  149. else
  150. debug("finish checkpointing, time to wake up all threads\n");
  151. DkEventSet(finish_event);
  152. return ret;
  153. }
  154. void * malloc_method_file (size_t size)
  155. {
  156. struct shim_handle * cpfile;
  157. master_lock();
  158. if (!current_cpsession || !current_cpsession->cpfile) {
  159. master_unlock();
  160. return NULL;
  161. }
  162. cpfile = current_cpsession->cpfile;
  163. get_handle(cpfile);
  164. master_unlock();
  165. struct shim_mount * fs = cpfile->fs;
  166. if (!fs || !fs->fs_ops ||
  167. !fs->fs_ops->truncate || !fs->fs_ops->mmap)
  168. return NULL;
  169. if (fs->fs_ops->truncate(cpfile, size) < 0)
  170. return NULL;
  171. void * addr = NULL;
  172. void * mem = fs->fs_ops->mmap(cpfile, &addr, ALIGN_UP(size),
  173. PROT_READ|PROT_WRITE,
  174. MAP_FILE|MAP_SHARED, 0) < 0 ? NULL : addr;
  175. put_handle(cpfile);
  176. return mem;
  177. }
  178. static int finish_checkpoint (void)
  179. {
  180. struct shim_cp_store cpstore;
  181. again:
  182. INIT_CP_STORE(&cpstore);
  183. BEGIN_MIGRATION_DEF(checkpoint)
  184. {
  185. store->use_gipc = false;
  186. DEFINE_MIGRATE(process, &cur_process, sizeof(struct shim_process),
  187. false);
  188. DEFINE_MIGRATE(all_mounts, NULL, 0, false);
  189. DEFINE_MIGRATE(all_vmas, NULL, 0, true);
  190. DEFINE_MIGRATE(all_running_threads, NULL, 0, true);
  191. DEFINE_MIGRATE(brk, NULL, 0, false);
  192. DEFINE_MIGRATE(loaded_libraries, NULL, 0, false);
  193. DEFINE_MIGRATE(gdb_map, NULL, 0, false);
  194. DEFINE_MIGRATE(migratable, NULL, 0, false);
  195. }
  196. END_MIGRATION_DEF
  197. int ret = START_MIGRATE(&cpstore, checkpoint, sizeof(struct cp_header));
  198. if (ret < 0)
  199. return ret;
  200. struct shim_cp_entry * cpent = cpstore.cpdata;
  201. for ( ; cpent->cp_type != CP_NULL ; cpent++)
  202. if (cpent->cp_type == CP_PALHDL &&
  203. cpent->cp_un.cp_val) {
  204. PAL_HANDLE * pal_hdl = cpstore.cpdata + cpent->cp_un.cp_val;
  205. assert(*pal_hdl);
  206. *pal_hdl = NULL;
  207. }
  208. struct cp_header * hdr = (struct cp_header *) cpstore.cpaddr;
  209. hdr->cpaddr = cpstore.cpaddr;
  210. hdr->cpsize = cpstore.cpsize;
  211. hdr->cpoffset = cpstore.cpdata - cpstore.cpaddr;
  212. DkStreamUnmap(cpstore.cpaddr, cpstore.cpsize);
  213. master_lock();
  214. assert(current_cpsession);
  215. struct shim_handle * cpfile = current_cpsession->cpfile;
  216. bool do_again = false;
  217. current_cpsession->cpfile = NULL;
  218. if (current_cpsession->list.next != &created_sessions) {
  219. current_cpsession = list_entry(current_cpsession->list.next,
  220. struct cpsession, list);
  221. do_again = true;
  222. } else {
  223. current_cpsession = NULL;
  224. }
  225. master_unlock();
  226. close_handle(cpfile);
  227. if (do_again)
  228. goto again;
  229. return 0;
  230. }
  231. int shim_do_checkpoint (const char * filename)
  232. {
  233. IDTYPE session = 0;
  234. int ret = 0;
  235. ret = shim_do_mkdir(filename, 0700);
  236. if (ret < 0)
  237. return ret;
  238. shim_tcb_t * tcb = SHIM_GET_TLS();
  239. assert(tcb && tcb->tp);
  240. struct shim_signal signal;
  241. __store_context(tcb, NULL, &signal);
  242. ret = create_checkpoint(filename, &session);
  243. if (ret < 0) {
  244. shim_do_rmdir(filename);
  245. return ret;
  246. }
  247. ipc_checkpoint_send(filename, session);
  248. kill_all_threads(tcb->tp, CHECKPOINT_REQUESTED, SIGINT);
  249. ret = join_checkpoint(tcb->tp, &signal.context);
  250. if (ret < 0) {
  251. shim_do_rmdir(filename);
  252. return ret;
  253. }
  254. return 0;
  255. }