syscall_server.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. #include "syscall_server.h"
  2. #include "core.h"
  3. #include "config.h"
  4. #include "config.hpp"
  5. #include "simulator.h"
  6. #include "hooks_manager.h"
  7. #include "thread_manager.h"
  8. #include "thread.h"
  9. #include "core_manager.h"
  10. #include "log.h"
  11. #include "circular_log.h"
  12. #include <sys/syscall.h>
  13. #include "os_compat.h"
  14. SyscallServer::SyscallServer()
  15. {
  16. m_reschedule_cost = SubsecondTime::NS() * Sim()->getCfg()->getInt("perf_model/sync/reschedule_cost");
  17. Sim()->getHooksManager()->registerHook(HookType::HOOK_PERIODIC, hook_periodic, (UInt64)this);
  18. }
  19. SyscallServer::~SyscallServer()
  20. {
  21. }
  22. void SyscallServer::handleSleepCall(thread_id_t thread_id, SubsecondTime wake_time, SubsecondTime curr_time, SubsecondTime &end_time)
  23. {
  24. ScopedLock sl(Sim()->getThreadManager()->getLock());
  25. m_sleeping.push_back(SimFutex::Waiter(thread_id, 0, wake_time));
  26. end_time = Sim()->getThreadManager()->stallThread(thread_id, ThreadManager::STALL_SLEEP, curr_time);
  27. }
  28. IntPtr SyscallServer::handleFutexCall(thread_id_t thread_id, futex_args_t &args, SubsecondTime curr_time, SubsecondTime &end_time)
  29. {
  30. ScopedLock sl(Sim()->getThreadManager()->getLock());
  31. CLOG("futex", "Futex enter thread %d", thread_id);
  32. int cmd = (args.op & FUTEX_CMD_MASK) & ~FUTEX_PRIVATE_FLAG;
  33. SubsecondTime timeout_time = SubsecondTime::MaxTime();
  34. LOG_PRINT("Futex syscall: uaddr(0x%x), op(%u), val(%u)", args.uaddr, args.op, args.val);
  35. Core* core = Sim()->getThreadManager()->getThreadFromID(thread_id)->getCore();
  36. LOG_ASSERT_ERROR (core != NULL, "Core should not be NULL");
  37. // Right now, we handle only a subset of the functionality
  38. // assert the subset
  39. LOG_ASSERT_ERROR((cmd == FUTEX_WAIT) \
  40. || (cmd == FUTEX_WAIT_BITSET) \
  41. || (cmd == FUTEX_WAKE) \
  42. || (cmd == FUTEX_WAKE_BITSET) \
  43. || (cmd == FUTEX_WAKE_OP) \
  44. || (cmd == FUTEX_CMP_REQUEUE) \
  45. , "op = 0x%x", args.op);
  46. if (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_BITSET)
  47. {
  48. if (args.timeout != NULL)
  49. {
  50. struct timespec timeout_buf;
  51. core->accessMemory(Core::NONE, Core::READ, (IntPtr) args.timeout, (char*) &timeout_buf, sizeof(timeout_buf));
  52. SubsecondTime timeout_val = SubsecondTime::SEC(timeout_buf.tv_sec - Sim()->getConfig()->getOSEmuTimeStart())
  53. + SubsecondTime::NS(timeout_buf.tv_nsec);
  54. if (cmd == FUTEX_WAIT_BITSET)
  55. {
  56. timeout_time = timeout_val; // FUTEX_WAIT_BITSET uses absolute timeout
  57. LOG_ASSERT_WARNING_ONCE(timeout_val < curr_time + SubsecondTime::SEC(10), "FUTEX_WAIT_BITSET timeout value is more than 10 seconds in the future");
  58. LOG_ASSERT_WARNING_ONCE(timeout_val > curr_time, "FUTEX_WAIT_BITSET timeout value is in the past");
  59. }
  60. else
  61. {
  62. timeout_time = curr_time + timeout_val; // FUTEX_WAIT uses relative timeout
  63. }
  64. }
  65. }
  66. int act_val;
  67. IntPtr res;
  68. core->accessMemory(Core::NONE, Core::READ, (IntPtr) args.uaddr, (char*) &act_val, sizeof(act_val));
  69. if (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_BITSET)
  70. {
  71. res = futexWait(thread_id, args.uaddr, args.val, act_val, cmd == FUTEX_WAIT_BITSET ? args.val3 : FUTEX_BITSET_MATCH_ANY, curr_time, timeout_time, end_time);
  72. }
  73. else if (cmd == FUTEX_WAKE || cmd == FUTEX_WAKE_BITSET)
  74. {
  75. res = futexWake(thread_id, args.uaddr, args.val, cmd == FUTEX_WAIT_BITSET ? args.val3 : FUTEX_BITSET_MATCH_ANY, curr_time, end_time);
  76. }
  77. else if (cmd == FUTEX_WAKE_OP)
  78. {
  79. res = futexWakeOp(thread_id, args.uaddr, args.uaddr2, args.val, args.val2, args.val3, curr_time, end_time);
  80. }
  81. else if(cmd == FUTEX_CMP_REQUEUE)
  82. {
  83. LOG_ASSERT_ERROR(args.uaddr != args.uaddr2, "FUTEX_CMP_REQUEUE: uaddr == uaddr2 == %p", args.uaddr);
  84. res = futexCmpRequeue(thread_id, args.uaddr, args.val, args.uaddr2, args.val3, act_val, curr_time, end_time);
  85. }
  86. else
  87. {
  88. LOG_PRINT_ERROR("Unknown SYS_futex cmd %d", cmd);
  89. }
  90. CLOG("futex", "Futex leave thread %d", thread_id);
  91. return res;
  92. }
  93. SubsecondTime SyscallServer::applyRescheduleCost(thread_id_t thread_id, bool conditional)
  94. {
  95. if (conditional == false)
  96. return SubsecondTime::Zero();
  97. else if (Sim()->getThreadManager()->getThreadFromID(thread_id)->getCore()->getInstructionCount() == 0)
  98. // Do not apply reschedule cost when we have not yet executed any instructions.
  99. // This would be the case in MPI mode before ROI.
  100. return SubsecondTime::Zero();
  101. else
  102. return m_reschedule_cost;
  103. }
  104. // -- Futex related functions --
  105. SimFutex* SyscallServer::findFutexByUaddr(int *uaddr, thread_id_t thread_id)
  106. {
  107. // Assumes that for multi-programmed and private futexes, va2pa() still returns unique addresses for each thread
  108. IntPtr address = Sim()->getThreadManager()->getThreadFromID(thread_id)->va2pa((IntPtr)uaddr);
  109. SimFutex *sim_futex = &m_futexes[address];
  110. return sim_futex;
  111. }
  112. IntPtr SyscallServer::futexWait(thread_id_t thread_id, int *uaddr, int val, int act_val, int mask, SubsecondTime curr_time, SubsecondTime timeout_time, SubsecondTime &end_time)
  113. {
  114. LOG_PRINT("Futex Wait");
  115. SimFutex *sim_futex = findFutexByUaddr(uaddr, thread_id);
  116. if (val != act_val)
  117. {
  118. end_time = curr_time;
  119. return -EWOULDBLOCK;
  120. }
  121. else
  122. {
  123. bool success = sim_futex->enqueueWaiter(thread_id, mask, curr_time, timeout_time, end_time);
  124. if (success)
  125. return 0;
  126. else
  127. return -ETIMEDOUT;
  128. }
  129. }
  130. thread_id_t SyscallServer::wakeFutexOne(SimFutex *sim_futex, thread_id_t thread_by, int mask, SubsecondTime curr_time)
  131. {
  132. thread_id_t waiter = sim_futex->dequeueWaiter(thread_by, mask, curr_time + applyRescheduleCost(thread_by));
  133. return waiter;
  134. }
  135. IntPtr SyscallServer::futexWake(thread_id_t thread_id, int *uaddr, int nr_wake, int mask, SubsecondTime curr_time, SubsecondTime &end_time)
  136. {
  137. LOG_PRINT("Futex Wake");
  138. SimFutex *sim_futex = findFutexByUaddr(uaddr, thread_id);
  139. int num_procs_woken_up = 0;
  140. for (int i = 0; i < nr_wake; i++)
  141. {
  142. thread_id_t waiter = wakeFutexOne(sim_futex, thread_id, mask, curr_time);
  143. if (waiter == INVALID_THREAD_ID)
  144. break;
  145. num_procs_woken_up ++;
  146. }
  147. end_time = curr_time + applyRescheduleCost(thread_id, num_procs_woken_up > 0);
  148. return num_procs_woken_up;
  149. }
  150. int SyscallServer::futexDoOp(Core *core, int encoded_op, int *uaddr)
  151. {
  152. int op = (encoded_op >> 28) & 7;
  153. int cmp = (encoded_op >> 24) & 15;
  154. int oparg = (encoded_op << 8) >> 20;
  155. int cmparg = (encoded_op << 20) >> 20;
  156. int oldval, newval;
  157. int ret = 0;
  158. if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
  159. oparg = 1 << oparg;
  160. core->accessMemory(Core::LOCK, Core::READ_EX, (IntPtr) uaddr, (char*) &oldval, sizeof(oldval));
  161. switch (op) {
  162. case FUTEX_OP_SET:
  163. newval = oparg;
  164. break;
  165. case FUTEX_OP_ADD:
  166. newval = oldval + oparg;
  167. break;
  168. case FUTEX_OP_OR:
  169. newval = oldval | oparg;
  170. break;
  171. case FUTEX_OP_ANDN:
  172. newval = oldval & ~oparg;
  173. break;
  174. case FUTEX_OP_XOR:
  175. newval = oldval ^ oparg;
  176. break;
  177. default:
  178. LOG_ASSERT_ERROR(false, "futexWakeOp: unknown op = %d", op);
  179. }
  180. if (op != FUTEX_OP_SET)
  181. {
  182. // TODO: Implement these using an atomic read-modify-write version of core->accessMemory
  183. LOG_PRINT_WARNING_ONCE("FUTEX_WAKE_OP is implemented non-atomically, race condition may have occured");
  184. }
  185. core->accessMemory(Core::UNLOCK, Core::WRITE, (IntPtr) uaddr, (char*) &newval, sizeof(newval));
  186. switch (cmp) {
  187. case FUTEX_OP_CMP_EQ:
  188. ret = (oldval == cmparg);
  189. break;
  190. case FUTEX_OP_CMP_NE:
  191. ret = (oldval != cmparg);
  192. break;
  193. case FUTEX_OP_CMP_LT:
  194. ret = (oldval < cmparg);
  195. break;
  196. case FUTEX_OP_CMP_GE:
  197. ret = (oldval >= cmparg);
  198. break;
  199. case FUTEX_OP_CMP_LE:
  200. ret = (oldval <= cmparg);
  201. break;
  202. case FUTEX_OP_CMP_GT:
  203. ret = (oldval > cmparg);
  204. break;
  205. default:
  206. LOG_ASSERT_ERROR(false, "futexWakeOp: unknown cmp = %d", cmp);
  207. }
  208. return ret;
  209. }
  210. IntPtr SyscallServer::futexWakeOp(thread_id_t thread_id, int *uaddr, int *uaddr2, int nr_wake, int nr_wake2, int op, SubsecondTime curr_time, SubsecondTime &end_time)
  211. {
  212. LOG_PRINT("Futex WakeOp");
  213. SimFutex *sim_futex = findFutexByUaddr(uaddr, thread_id);
  214. SimFutex *sim_futex2 = findFutexByUaddr(uaddr2, thread_id);
  215. int num_procs_woken_up = 0;
  216. Thread *thread = Sim()->getThreadManager()->getThreadFromID(thread_id);
  217. Core *core = thread->getCore();
  218. LOG_ASSERT_ERROR(core != NULL, "Cannot execute futexWakeOp() for a thread that is unscheduled");
  219. int op_ret = futexDoOp(core, op, uaddr2);
  220. for (int i = 0; i < nr_wake; i++)
  221. {
  222. thread_id_t waiter = wakeFutexOne(sim_futex, thread_id, FUTEX_BITSET_MATCH_ANY, curr_time);
  223. if (waiter == INVALID_THREAD_ID)
  224. break;
  225. num_procs_woken_up ++;
  226. }
  227. if (op_ret > 0) {
  228. for (int i = 0; i < nr_wake2; i++)
  229. {
  230. thread_id_t waiter = wakeFutexOne(sim_futex2, thread_id, FUTEX_BITSET_MATCH_ANY, curr_time);
  231. if (waiter == INVALID_THREAD_ID)
  232. break;
  233. num_procs_woken_up ++;
  234. }
  235. }
  236. end_time = curr_time + applyRescheduleCost(thread_id, num_procs_woken_up > 0);
  237. return num_procs_woken_up;
  238. }
  239. IntPtr SyscallServer::futexCmpRequeue(thread_id_t thread_id, int *uaddr, int val, int *uaddr2, int val3, int act_val, SubsecondTime curr_time, SubsecondTime &end_time)
  240. {
  241. LOG_PRINT("Futex CMP_REQUEUE");
  242. SimFutex *sim_futex = findFutexByUaddr(uaddr, thread_id);
  243. int num_procs_woken_up = 0;
  244. if(val3 != act_val)
  245. {
  246. end_time = curr_time;
  247. return -EAGAIN;
  248. }
  249. else
  250. {
  251. for(int i = 0; i < val; i++)
  252. {
  253. thread_id_t waiter = sim_futex->dequeueWaiter(thread_id, FUTEX_BITSET_MATCH_ANY, curr_time);
  254. if(waiter == INVALID_THREAD_ID)
  255. break;
  256. num_procs_woken_up++;
  257. }
  258. SimFutex *requeue_futex = findFutexByUaddr(uaddr2, thread_id);
  259. while(true)
  260. {
  261. // dequeueWaiter changes the thread state to
  262. // RUNNING, which is changed back to STALLED
  263. // by enqueueWaiter. Since only the MCP uses this state
  264. // this should be okay.
  265. thread_id_t waiter = sim_futex->requeueWaiter(requeue_futex);
  266. if(waiter == INVALID_THREAD_ID)
  267. break;
  268. }
  269. end_time = curr_time;
  270. return num_procs_woken_up;
  271. }
  272. }
  273. void SyscallServer::futexPeriodic(SubsecondTime time)
  274. {
  275. // Wake sleeping threads
  276. for(SimFutex::ThreadQueue::iterator it = m_sleeping.begin(); it != m_sleeping.end(); ++it)
  277. {
  278. if (it->timeout <= time)
  279. {
  280. thread_id_t waiter = it->thread_id;
  281. m_sleeping.erase(it);
  282. Sim()->getThreadManager()->resumeThread(waiter, waiter, time, (void*)false);
  283. // Iterator will be invalid, wake up potential others in the next barrier synchronization which should only be 100ns away
  284. break;
  285. }
  286. }
  287. // Wake timeout futexes
  288. for(FutexMap::iterator it = m_futexes.begin(); it != m_futexes.end(); ++it)
  289. {
  290. it->second.wakeTimedOut(time);
  291. }
  292. }
  293. SubsecondTime SyscallServer::getNextTimeout(SubsecondTime time)
  294. {
  295. SubsecondTime next = SubsecondTime::MaxTime();
  296. // Sleeping threads
  297. for(SimFutex::ThreadQueue::iterator it = m_sleeping.begin(); it != m_sleeping.end(); ++it)
  298. {
  299. if (it->timeout < SubsecondTime::MaxTime())
  300. next = it->timeout;
  301. }
  302. // Timeout futexes
  303. for(FutexMap::iterator it = m_futexes.begin(); it != m_futexes.end(); ++it)
  304. {
  305. SubsecondTime t = it->second.getNextTimeout(time);
  306. if (t < next)
  307. next = t;
  308. }
  309. return next;
  310. }
  311. // -- SimFutex -- //
  312. SimFutex::SimFutex()
  313. {}
  314. SimFutex::~SimFutex()
  315. {
  316. #if 0 // Disabled: applications are not required to do proper cleanup
  317. if (!m_waiting.empty())
  318. {
  319. printf("Threads still waiting for futex %p: ", this);
  320. while(!m_waiting.empty())
  321. {
  322. printf("%u ", m_waiting.front().thread_id);
  323. m_waiting.pop_front();
  324. }
  325. printf("\n");
  326. }
  327. #endif
  328. }
  329. bool SimFutex::enqueueWaiter(thread_id_t thread_id, int mask, SubsecondTime time, SubsecondTime timeout_time, SubsecondTime &time_end)
  330. {
  331. m_waiting.push_back(Waiter(thread_id, mask, timeout_time));
  332. time_end = Sim()->getThreadManager()->stallThread(thread_id, ThreadManager::STALL_FUTEX, time);
  333. return Sim()->getThreadManager()->getThreadFromID(thread_id)->getWakeupMsg();
  334. }
  335. thread_id_t SimFutex::dequeueWaiter(thread_id_t thread_by, int mask, SubsecondTime time)
  336. {
  337. if (m_waiting.empty())
  338. return INVALID_THREAD_ID;
  339. else
  340. {
  341. for(ThreadQueue::iterator it = m_waiting.begin(); it != m_waiting.end(); ++it)
  342. {
  343. if (mask & it->mask)
  344. {
  345. thread_id_t waiter = it->thread_id;
  346. m_waiting.erase(it);
  347. Sim()->getThreadManager()->resumeThread(waiter, thread_by, time, (void*)true);
  348. return waiter;
  349. }
  350. }
  351. return INVALID_THREAD_ID;
  352. }
  353. }
  354. thread_id_t SimFutex::requeueWaiter(SimFutex *requeue_futex)
  355. {
  356. if (m_waiting.empty())
  357. return INVALID_THREAD_ID;
  358. else
  359. {
  360. Waiter waiter = m_waiting.front();
  361. m_waiting.pop_front();
  362. requeue_futex->m_waiting.push_back(waiter);
  363. return waiter.thread_id;
  364. }
  365. }
  366. void SimFutex::wakeTimedOut(SubsecondTime time)
  367. {
  368. for(ThreadQueue::iterator it = m_waiting.begin(); it != m_waiting.end(); ++it)
  369. {
  370. if (it->timeout <= time)
  371. {
  372. thread_id_t waiter = it->thread_id;
  373. m_waiting.erase(it);
  374. Sim()->getThreadManager()->resumeThread(waiter, INVALID_THREAD_ID, time, (void*)false);
  375. // Iterator will be invalid, wake up potential others in the next barrier synchronization which should only be 100ns away
  376. break;
  377. }
  378. }
  379. }
  380. SubsecondTime SimFutex::getNextTimeout(SubsecondTime time)
  381. {
  382. SubsecondTime next = SubsecondTime::MaxTime();
  383. for(ThreadQueue::iterator it = m_waiting.begin(); it != m_waiting.end(); ++it)
  384. {
  385. if (it->timeout < SubsecondTime::MaxTime())
  386. next = it->timeout;
  387. }
  388. return next;
  389. }