The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
ecs.c
Go to the documentation of this file.
1 
32 #ifdef HAVE_CROSS_STATE
33 
34 #include <unistd.h>
35 #include <stddef.h>
36 #include <string.h>
37 #include <signal.h>
38 #include <sys/mman.h>
39 #include <sys/mman.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <sys/types.h>
43 
44 #if defined(OS_LINUX)
45 #include <stropts.h>
46 #endif
47 
48 
49 #include <core/core.h>
50 #include <mm/mm.h>
51 #include <scheduler/scheduler.h>
52 #include <scheduler/process.h>
54 #include <arch/ult.h>
55 #include <arch/x86/disassemble.h>
56 
58 
59 #define __NR_OPEN 2
60 
61 void (*callback_function)(void);
62 
65 
66 static int ioctl_fd;
67 
69 static __thread int pgd_ds;
70 
72 static __thread fault_info_t fault_info;
73 
74 // Declared in ecsstub.S
75 extern void rootsim_cross_state_dependency_handler(void);
76 
77 // This handler is only called in case of a remote ECS
78 void ecs_secondary(void) {
79 
80  GID_t target_gid;
81  // target_address is filled by the ROOT-Sim fault handler at kernel level before triggering the signal
82  long long target_address = fault_info.target_address;
83  unsigned char *faulting_insn = (unsigned char *)fault_info.rip;
84  long long span;
85  insn_info_x86 insn_disasm;
86  unsigned long i=0;
87  msg_t *control_msg;
88  ecs_page_request_t page_req;
89 
90  // Disassemble the faulting instruction to get necessary information
91  x86_disassemble_instruction(faulting_insn, &i, &insn_disasm, DATA_64 | ADDR_64);
92 
93  if(!IS_STRING(&insn_disasm)) {
94  span = insn_disasm.span;
95  } else {
96  span = insn_disasm.span * fault_info.rcx;
97  }
98 
99  // Compute the starting page address and the page count to get the lease
100  page_req.write_mode = IS_MEMWR(&insn_disasm);
101  page_req.base_address = (void *)(target_address & (~((long long)PAGE_SIZE-1)));
102  page_req.count = ((target_address + span) & (~((long long)PAGE_SIZE-1)))/PAGE_SIZE - (long long)page_req.base_address/PAGE_SIZE + 1;
103 
104 // printf("ECS Page Fault: LP %d accessing %d pages from %p on LP %lu in %s mode\n", current->gid, page_req.count, (void *)page_req.base_address, fault_info.target_gid, (page_req.write_mode ? "write" : "read"));
105  fflush(stdout);
106 
107  // Send the page lease request control message. This is not incorporated into the input queue at the receiver
108  // so we do not place it into the output queue
109  target_gid.id = fault_info.target_gid;
110  pack_msg(&control_msg, current->gid, target_gid, RENDEZVOUS_GET_PAGE, current_lvt, current_lvt, sizeof(page_req), &page_req);
111  control_msg->rendezvous_mark = current_evt->rendezvous_mark;
112  Send(control_msg);
113 
114  current->state = LP_STATE_WAIT_FOR_DATA;
115 
116  // Give back control to the simulation kernel's user-level thread
118 }
119 
120 void ecs_initiate(void) {
121  msg_t *control_msg;
122  msg_hdr_t *msg_hdr;
123 
124  GID_t target_gid;
125  // Generate a unique mark for this ECS
127  current->wait_on_rendezvous = current_evt->rendezvous_mark;
128 
129  // Prepare the control message to synchronize the two LPs
130  target_gid.id = fault_info.target_gid;
131  pack_msg(&control_msg, current->gid, target_gid, RENDEZVOUS_START, current_lvt, current_lvt, 0, NULL);
133  control_msg->mark = generate_mark(current);
134 
135  // This message must be stored in the output queue as well, in case this LP rollbacks
136  msg_hdr = get_msg_hdr_from_slab();
137  msg_to_hdr(msg_hdr, control_msg);
138  list_insert(current->queue_out, send_time, msg_hdr);
139 
140  // Block the execution of this LP
141  current->state = LP_STATE_WAIT_FOR_SYNCH;
142  current->wait_on_object = fault_info.target_gid;
143 
144  // Store which LP we are waiting for synchronization.
145  current->ECS_index++;
146  current->ECS_synch_table[current->ECS_index] = target_gid;
147  Send(control_msg);
148 
149  // Give back control to the simulation kernel's user-level thread
151 }
152 
153 // This handler is called to initiate an ECS, both in the local and in the distributed case
154 void ECS(void) __attribute__((__used__));
155 void ECS(void) {
156  // ECS cannot happen in silent execution, as we take a log after the completion
157  // of an event which involves one or multiple ecs
158  if(unlikely(current->state == LP_STATE_SILENT_EXEC)) {
159  rootsim_error(true,"----ERROR---- ECS in Silent Execution LP[%d] Hit:%llu Timestamp:%f\n",
160  current->lid.to_int, fault_info.target_gid, current_lvt);
161  }
162 
163  // Sanity check: we cannot run an ECS with an old mark after a rollback
164  if(current->wait_on_rendezvous != 0 && current->wait_on_rendezvous != current_evt->rendezvous_mark) {
165  printf("muori male\n");
166  fflush(stdout);
167  abort();
168  }
169 
170 // printf("Entro nell'ECS handler per un fault di tipo %d\n", fault_info.fault_type);
171 
172  switch(fault_info.fault_type) {
173 
174  case ECS_MAJOR_FAULT:
175  ecs_initiate();
176  break;
177 
178  case ECS_MINOR_FAULT:
179  // TODO: gestire qui la read/write list!!!!
180  ecs_secondary();
181  break;
182 
183  case ECS_CHANGE_PAGE_PRIVILEGE:
184  // TODO: gestire qui la write list!!!!
185  lp_alloc_schedule(); // We moved to the original view in the kernel module: we do not unschedule the LP here
186  break;
187 
188  default:
189  rootsim_error(true, "Impossible condition! Aborting...\n");
190  return;
191  }
192 }
193 
194 void ecs_init(void) {
195  //printf("Invocation of ECS Init\n");
196  ioctl_fd = open("/dev/ktblmgr", O_RDONLY);
197  if (ioctl_fd <= -1) {
198  rootsim_error(true, "Error in opening special device file. ROOT-Sim is compiled for using the ktblmgr linux kernel module, which seems to be not loaded.");
199  }
200 }
201 
202 // inserire qui tutte le api di schedulazione/deschedulazione
203 void lp_alloc_thread_init(void) {
204  void *ptr;
205  GID_t LP0; //dummy structure just to accomplish get_base_pointer
206 
207  LP0.id = 0;
208  // TODO: test ioctl return value
209  ioctl(ioctl_fd, IOCTL_SET_ANCESTOR_PGD,NULL); //ioctl call
210  lp_memory_ioctl_info.ds = -1;
211  ptr = get_base_pointer(LP0); // LP 0 is the first allocated one, and it's memory stock starts from the beginning of the PML4
212  lp_memory_ioctl_info.addr = ptr;
213  lp_memory_ioctl_info.mapped_processes = n_prc_tot;
214 
215  callback_function = rootsim_cross_state_dependency_handler;
216  lp_memory_ioctl_info.callback = (ulong) callback_function;
217 
218  // TODO: this function is called by each worker thread. Does calling SET_VM_RANGE cause
219  // a memory leak into kernel space?
220  // TODO: Yes it does! And there could be some issues when unmounting as well!
221  ioctl(ioctl_fd, IOCTL_SET_VM_RANGE, &lp_memory_ioctl_info);
222 
223  /* required to manage the per-thread memory view */
224  pgd_ds = ioctl(ioctl_fd, IOCTL_GET_PGD, &fault_info); //ioctl call
225 }
226 
227 void lp_alloc_schedule(void) {
228 
229  ioctl_info sched_info;
230  bzero(&sched_info, sizeof(ioctl_info));
231 
232  sched_info.ds = pgd_ds;
233  sched_info.count = current->ECS_index + 1; // it's a counter
234  sched_info.objects = (unsigned int*) current->ECS_synch_table; // pgd descriptor range from 0 to number threads - a subset of object ids
235 
236  /* passing into LP mode - here for the pgd_ds-th LP */
237  ioctl(ioctl_fd,IOCTL_SCHEDULE_ON_PGD, &sched_info);
238 }
239 
240 
241 void lp_alloc_deschedule(void) {
242  /* stepping back into non-LP mode */
243  /* all previously scheduled (memory opened) LPs are also unscheduled by default */
244  /* reaccessing their state will give rise to traps (if not scheduled again) */
245  ioctl(ioctl_fd,IOCTL_UNSCHEDULE_ON_PGD, pgd_ds);
246 }
247 
248 void setup_ecs_on_segment(msg_t *msg) {
249  ioctl_info sched_info;
250 
251 // printf("Eseguo setup_ecs_on_segment per il messaggio:\n");
252 // dump_msg_content(msg);
253 
254  // In case of a remote ECS, protect the memory
255  if(find_kernel_by_gid(msg->sender) != kid) {
256  //printf("Mi sincronizzo con un LP remoto e proteggo la memoria\n");
257  bzero(&sched_info, sizeof(ioctl_info));
258  sched_info.base_address = get_base_pointer(msg->sender);
259  ioctl(ioctl_fd, IOCTL_PROTECT_REMOTE_LP, &sched_info);
260  }
261 }
262 
263 void lp_alloc_thread_fini(void) {
264 }
265 
266 void ecs_send_pages(msg_t *msg) {
267  msg_t *control_msg;
268  ecs_page_request_t *the_request;
269  ecs_page_request_t *the_pages;
270 
271  the_request = (ecs_page_request_t *)&(msg->event_content);
272  the_pages = rsalloc(sizeof(ecs_page_request_t) + the_request->count * PAGE_SIZE);
273  the_pages->write_mode = the_request->write_mode;
274  the_pages->base_address = the_request->base_address;
275  the_pages->count = the_request->count;
276 
277  //printf("LP %d sending %d pages from %p to %d\n", msg->receiver, the_request->count, the_request->base_address, msg->sender);
278  fflush(stdout);
279 
280  memcpy(the_pages->buffer, the_request->base_address, the_request->count * PAGE_SIZE);
281 
282  // Send back a copy of the pages!
283  pack_msg(&control_msg, msg->receiver, msg->sender, RENDEZVOUS_GET_PAGE_ACK, msg->timestamp, msg->timestamp, sizeof(ecs_page_request_t)+the_pages->count * PAGE_SIZE, the_pages);
284  control_msg->mark = generate_mark(GidToLid(msg->receiver));
285  control_msg->rendezvous_mark = msg->rendezvous_mark;
286  Send(control_msg);
287 
288  rsfree(the_pages);
289 }
290 
291 void ecs_install_pages(msg_t *msg) {
292  ecs_page_request_t *the_pages = (ecs_page_request_t *)&(msg->event_content);
293  ioctl_info sched_info;
294 
295  //printf("LP %d receiving %d pages from %p from %d\n", msg->receiver, the_pages->count, the_pages->base_address, msg->sender);
296  fflush(stdout);
297 
298  memcpy(the_pages->base_address, the_pages->buffer, the_pages->count * PAGE_SIZE);
299 
300  //printf("Completed the installation of the page copying %d bytes\n", the_pages->count * PAGE_SIZE);
301  fflush(stdout);
302 
303  bzero(&sched_info, sizeof(ioctl_info));
304  sched_info.base_address = the_pages->base_address;
305  sched_info.page_count = the_pages->count;
306  sched_info.write_mode = the_pages->write_mode;
307 
308  // TODO: se accedo in write non devo fare questa chiamata!
309 // ioctl(ioctl_fd, IOCTL_SET_PAGE_PRIVILEGE, &sched_info);
310 
311  //printf("Completato il setup dei privilegi\n");
312  fflush(stdout);
313 }
314 
315 void unblock_synchronized_objects(struct lp_struct *lp) {
316  unsigned int i;
317  msg_t *control_msg;
318 
319  for(i = 1; i <= lp->ECS_index; i++) {
320  pack_msg(&control_msg, LidToGid(localID), lp->ECS_synch_table[i], RENDEZVOUS_UNBLOCK, lvt(localID), lvt(localID), 0, NULL);
321  control_msg->rendezvous_mark = lp->wait_on_rendezvous;
322  Send(control_msg);
323  }
324 
325  lp->wait_on_rendezvous = 0;
326  lp->ECS_index = 0;
327 }
328 
329 void remote_memory_init(void) {
330  foreach_lp(lp) {
331  if(find_kernel_by_gid(lp->gid) != kid) {
332  (void)get_segment(lp->gid);
333  }
334  }
335 }
336 #endif
Per-thread page table.
ECS protocol: a remote LP is asked for a certain set of pages.
Definition: communication.h:69
Communication Routines.
#define lvt(lp)
Definition: process.h:168
unsigned long long generate_mark(struct lp_struct *lp)
Definition: queues.c:270
User-Level Threads Headers.
#define ADDR_64
Address fields in instructions are 64-bits.
Definition: disassemble.h:56
x86 ISA disassembler header
Core ROOT-Sim functionalities.
void msg_to_hdr(msg_hdr_t *hdr, msg_t *msg)
Convert a message to a message header.
unsigned int to_int
The LID numerical value.
Definition: core.h:145
The ROOT-Sim scheduler main module header.
__thread struct lp_struct * current
This is a per-thread variable pointing to the block state of the LP currently scheduled.
Definition: scheduler.c:72
unsigned int find_kernel_by_gid(GID_t gid)
Definition: core.c:164
ECS protocol: start synchronizing two LPs for a page fault.
Definition: communication.h:65
#define list_insert(li, key_name, data)
Insert a new node in the list.
Definition: list.h:163
__thread msg_t * current_evt
Definition: scheduler.c:83
Memory Manager main header.
#define long_jmp(env, val)
Definition: jmp.h:109
Message Type definition.
Definition: core.h:164
#define DATA_64
Data fields in instructions are 64-bits.
Definition: disassemble.h:54
__thread kernel_context_t kernel_context
This is the execution context of the simulation kernel.
Definition: ult.c:51
ECS protocol: the destination LP can resume its normal execution.
Definition: communication.h:67
void Send(msg_t *msg)
Send a message.
LP control blocks.
unsigned long long rendezvous_mark
Unique identifier of the message, used for antimessages.
Definition: core.h:186
void pack_msg(msg_t **msg, GID_t sender, GID_t receiver, int type, simtime_t timestamp, simtime_t send_time, size_t size, void *payload)
Pack a message in a platform-level data structure.
Definition of a GID.
Definition: core.h:132
GID_t gid
Global ID of the LP.
Definition: process.h:82
short unsigned int state
Current execution state of the LP.
Definition: process.h:88
LID_t lid
Local ID of the LP.
Definition: process.h:79
static __thread fault_info_t fault_info
Per Worker-Thread Fault Info Structure.
Definition: ecs.c:72
Message envelope definition. This is used to handle the output queue and stores information needed to...
Definition: core.h:194
ECS protocol: the sender LP is giving a lease on a set of pages.
Definition: communication.h:70
static __thread int pgd_ds
Per Worker-Thread Memory View.
Definition: ecs.c:69
static ioctl_info lp_memory_ioctl_info
This variable keeps track of information needed by the Linux Kernel Module for activating cross-LP me...
Definition: ecs.c:64
unsigned int n_prc_tot
Total number of logical processes running in the simulation.
Definition: core.c:64
msg_hdr_t * get_msg_hdr_from_slab(struct lp_struct *lp)
Get a buffer to keep a message header.
#define unlikely(exp)
Optimize the branch as likely not taken.
Definition: core.h:74
unsigned int kid
Identifier of the local kernel.
Definition: core.c:55