/models/cache/mcp-cache/coherence/MESI_client.cpp

https://gitlab.com/pranith/manifold · C++ · 575 lines · 400 code · 61 blank · 114 comment · 14 complexity · 3e664b2d2ad6cc7667e7c0786ba21107 MD5 · raw file

  1. #include "MESI_client.h"
  2. #include <assert.h>
  3. #include <cstdio>
  4. #include <iostream>
  5. //! There are 8 cases where the client has conflicting requests:
  6. //! | client state | MC_FWD_E | MC_FWD_S | MC_DEMAND_I
  7. //! ----------|--------------|----------|----------|-------------
  8. //! CM_I_to_E | IE | NA | NA | conflict*
  9. //! ----------|--------------|----------|----------|-------------
  10. //! CM_I_to_S | IE | NA | NA | NA
  11. //! ----------|--------------|----------|----------|-------------
  12. //! CM_E_to_I | EI | conflict | conflict | conflict
  13. //! ----------|--------------|----------|----------|-------------
  14. //! CM_M_to_I | MI | conflict | conflict | conflict
  15. //!
  16. //! Note 1: Another is between CM_I_to_E and MC_DEMAND_I, when the CM_I_to_E really means
  17. //! CM_S_to_E, sent when a client in S is being written to.
  18. //!
  19. //! Note 2: Client in IE could also get a DEMAND_I. If a line in S is evicted, manager is
  20. //! not notified. Later if the SAME line is accessed, an I_to_E is sent to manager. If before
  21. //! processing the I_to_E, manager starts to evict the line, since manager thinks the client
  22. //! is a sharer, it sends DEMAND_I, so client gets DEMAND_I in IE state.
  23. //!
  24. //!
  25. //! For example, the conflict between CM_E_to_I and MC_FWD_E happens as follows:
  26. //! Manager gets a write request from a client. Since it's in E, it sends FWD_E
  27. //! to owner. Before the owner receives the FWD_E, it starts evicting the line,
  28. //! so it sends CM_E_to_I to manager. Here is how the manager and client deal
  29. //! with the situation:
  30. //! Client: manager's request takes precedence, so it processes FWD_E in EI state
  31. //! as if it were in E state. Then enters I state.
  32. //! Manager: manager is unaware of the conflict that's occurring. After the store
  33. //! request is complete, it enters E state with owner set to the new
  34. //! requestor. Now it processes CM_E_to_I in E state, but it will notice
  35. //! the sender is not the owner, so it must be the old owner. It simply
  36. //! ignores the request since client already enters I state.
  37. using namespace std;
  38. namespace manifold {
  39. namespace mcp_cache_namespace {
  40. /** Constructor */
  41. MESI_client::MESI_client(int id) : ClientInterface(id)
  42. {
  43. state = MESI_C_I;
  44. }
  45. /** Destructor */
  46. MESI_client::~MESI_client()
  47. {
  48. }
  49. /** Client begins data and read permission acquistion within its native coherence realm L1/Lower Manager expects GrantReadD upon completion */
  50. void MESI_client::GetReadD()
  51. {
  52. //process(new cache_req(my_entry->tag, -1, GET_S, -1));
  53. process(GET_S, -1);
  54. }
  55. /** Client begins data and write permission acquistion within its native coherence realm L1/Lower Manager expects GrantWriteD upon completion */
  56. void MESI_client::GetWriteD()
  57. {
  58. //process(new cache_req(my_entry->tag, -1, GET_E, -1));
  59. process(GET_E, -1);
  60. }
  61. /** Client begins write permission acquistion within its native coherence realm L1/Lower Manager expects GrantWrite or GrantWriteD upon completion */
  62. void MESI_client::GetWrite()
  63. {
  64. //process(new cache_req(my_entry->tag, -1, GET_E, -1));
  65. process(GET_E, -1);
  66. }
  67. /** Client begins eviction sequence within its native coherence realm L1/Lower Manager expects GrantWrite or GrantWriteD upon completion */
  68. void MESI_client::GetEvict()
  69. {
  70. //process(new cache_req(my_entry->tag, -1, GET_EVICT, -1));
  71. process(GET_EVICT, -1);
  72. }
  73. /** Returns true if data exists or can be supplied to the manager */
  74. bool MESI_client::HaveData()
  75. {
  76. bool ret = false;
  77. switch(state)
  78. {
  79. case MESI_C_I:
  80. break;
  81. case MESI_C_E:
  82. case MESI_C_S:
  83. case MESI_C_M:
  84. ret = true;
  85. break;
  86. default:
  87. // transient states?
  88. invalid_state();
  89. break;
  90. }
  91. return ret;
  92. }
  93. /** Returns true if you have read permissions */
  94. bool MESI_client::HaveReadP()
  95. {
  96. bool ret = false;
  97. switch(state)
  98. {
  99. case MESI_C_I:
  100. break;
  101. case MESI_C_E:
  102. case MESI_C_S:
  103. case MESI_C_M:
  104. ret = true;
  105. break;
  106. default:
  107. invalid_state();
  108. break;
  109. }
  110. return ret;
  111. }
  112. /** Returns true if you have write permissions */
  113. bool MESI_client::HaveWriteP()
  114. {
  115. bool ret = false;
  116. switch(state)
  117. {
  118. case MESI_C_I:
  119. case MESI_C_S:
  120. case MESI_C_E:
  121. break;
  122. case MESI_C_M:
  123. ret = true;
  124. break;
  125. default:
  126. //invalid_state();
  127. break;
  128. }
  129. return ret;
  130. }
  131. /** Returns true if you can be safely evicted */
  132. bool MESI_client::HaveEvictP()
  133. {
  134. bool ret = false;
  135. switch(state)
  136. {
  137. case MESI_C_I:
  138. ret = true;
  139. break;
  140. case MESI_C_S:
  141. case MESI_C_E:
  142. case MESI_C_M:
  143. break;
  144. default:
  145. //invalid_state();
  146. break;
  147. }
  148. return ret;
  149. }
  150. /** Called by the Manager to complete previous Supply demand. Supplies data packet */
  151. void MESI_client::SupplyAck()
  152. {
  153. }
  154. /** Called by the Manager to complete previous Invalidate demand. Signifies realm invalidation has completed */
  155. void MESI_client::InvalidateAck()
  156. {
  157. }
  158. /** Called by the Manager to complete previous SupplyDowngrade demand. Supplies data packet and signifies realm downgrade has completed */
  159. void MESI_client::SupplyDowngradeAck()
  160. {
  161. }
  162. /** Called by the Manager to complete previous SupplyInvalidate demand. Supplies data packet and signifies realm invalidation has completed */
  163. void MESI_client::SupplyInvalidateAck()
  164. {
  165. }
  166. bool MESI_client :: req_pending()
  167. {
  168. switch(state) {
  169. case MESI_C_M:
  170. case MESI_C_E:
  171. case MESI_C_S:
  172. case MESI_C_I:
  173. return false;
  174. default:
  175. return true;
  176. }
  177. }
  178. //! @param \c fwdID The ID of the client to forward msg to. If no forward, can be any value.
  179. void MESI_client::process(int msg, int fwdID)
  180. {
  181. MESI_messages_t m = (MESI_messages_t) msg;
  182. switch(state)
  183. {
  184. case MESI_C_I:
  185. do_I(m);
  186. break;
  187. case MESI_C_S:
  188. do_S(m);
  189. break;
  190. case MESI_C_E:
  191. do_E(m, fwdID);
  192. break;
  193. case MESI_C_M:
  194. do_M(m, fwdID);
  195. break;
  196. case MESI_C_IE:
  197. do_IE(m);
  198. break;
  199. case MESI_C_EI:
  200. do_EI(m, fwdID);
  201. break;
  202. case MESI_C_MI:
  203. do_MI(m, fwdID);
  204. break;
  205. case MESI_C_SE:
  206. do_SE(m);
  207. break;
  208. //case MESI_C_SIE:
  209. //do_SIE(m);
  210. //break;
  211. default:
  212. invalid_msg((MESI_messages_t)msg);
  213. break;
  214. }
  215. }
  216. inline void MESI_client::do_I(MESI_messages_t msg)
  217. {
  218. #ifdef DBG_MESI_CLIENT
  219. std::cout << "Client in state I, msg= " << msg << std::endl;
  220. #endif
  221. switch (msg)
  222. {
  223. case GET_E:
  224. sendmsg(true, MESI_CM_I_to_E);
  225. transition_to_ie();
  226. break;
  227. case GET_S:
  228. sendmsg(true, MESI_CM_I_to_S);
  229. transition_to_ie();
  230. break;
  231. default:
  232. invalid_msg(msg);
  233. break;
  234. }
  235. }
  236. inline void MESI_client::do_S(MESI_messages_t msg)
  237. {
  238. #ifdef DBG_MESI_CLIENT
  239. std::cout << "Client in state S, msg= " << msg << std::endl;
  240. #endif
  241. switch (msg)
  242. {
  243. case GET_E:
  244. sendmsg(true, MESI_CM_I_to_E); //should we create a new message MESI_CM_S_to_E and
  245. //use it instead ??????????????????????????????
  246. //transition_to_sie();
  247. transition_to_se(); //go to SE and wait for GRANT_E, as manager won't send DEMAND_I to us.
  248. break;
  249. case GET_S:
  250. break;
  251. case GET_EVICT:
  252. transition_to_i();
  253. break;
  254. case MESI_MC_DEMAND_I:
  255. sendmsg(false, MESI_CM_UNBLOCK_I);
  256. transition_to_i();
  257. break;
  258. default:
  259. invalid_msg(msg);
  260. break;
  261. }
  262. }
  263. inline void MESI_client::do_E(MESI_messages_t msg, int fwdID)
  264. {
  265. #ifdef DBG_MESI_CLIENT
  266. std::cout << "Client in state E, msg= " << msg << std::endl;
  267. #endif
  268. switch (msg)
  269. {
  270. case GET_E:
  271. transition_to_m();
  272. break;
  273. case GET_S:
  274. break;
  275. case MESI_MC_FWD_E:
  276. sendmsg(false, MESI_CC_E_DATA, fwdID); // send to client in IE.
  277. transition_to_i();
  278. break;
  279. case MESI_MC_FWD_S:
  280. sendmsg(false, MESI_CC_S_DATA, fwdID); // send to client in IE.
  281. sendmsg(false, MESI_CM_CLEAN);
  282. transition_to_s();
  283. break;
  284. case GET_EVICT:
  285. sendmsg(true, MESI_CM_E_to_I);
  286. transition_to_ei();
  287. break;
  288. case MESI_MC_DEMAND_I:
  289. sendmsg(false, MESI_CM_UNBLOCK_I);
  290. transition_to_i();
  291. break;
  292. default:
  293. invalid_msg(msg);
  294. break;
  295. }
  296. }
  297. inline void MESI_client::do_M(MESI_messages_t msg, int fwdID)
  298. {
  299. #ifdef DBG_MESI_CLIENT
  300. std::cout << "Client in state M, msg= " << msg << std::endl;
  301. #endif
  302. switch (msg)
  303. {
  304. case GET_E:
  305. case GET_S:
  306. break;
  307. case MESI_MC_DEMAND_I:
  308. sendmsg(false, MESI_CM_UNBLOCK_I_DIRTY);
  309. transition_to_i();
  310. break;
  311. case GET_EVICT:
  312. sendmsg(true, MESI_CM_M_to_I);
  313. transition_to_mi();
  314. break;
  315. case MESI_MC_FWD_E:
  316. sendmsg(false, MESI_CC_M_DATA, fwdID);
  317. transition_to_i();
  318. break;
  319. case MESI_MC_FWD_S:
  320. sendmsg(false, MESI_CC_S_DATA, fwdID);
  321. sendmsg(false, MESI_CM_WRITEBACK);
  322. transition_to_s();
  323. break;
  324. default:
  325. invalid_msg(msg);
  326. break;
  327. }
  328. }
  329. inline void MESI_client::do_IE(MESI_messages_t msg)
  330. {
  331. #ifdef DBG_MESI_CLIENT
  332. std::cout << "Client " << id << " in state IE, msg= " << msg << std::endl;
  333. #endif
  334. switch (msg)
  335. {
  336. case MESI_CC_E_DATA: // data forwarded to me
  337. sendmsg(false, MESI_CM_UNBLOCK_E);
  338. transition_to_e();
  339. break;
  340. case MESI_CC_S_DATA: // data forwarded to me
  341. sendmsg(false, MESI_CM_UNBLOCK_S);
  342. transition_to_s();
  343. break;
  344. case MESI_MC_GRANT_E_DATA: // data given to me by manager
  345. sendmsg(false, MESI_CM_UNBLOCK_E);
  346. transition_to_e();
  347. break;
  348. case MESI_MC_GRANT_S_DATA: // sharing
  349. sendmsg(false, MESI_CM_UNBLOCK_S);
  350. transition_to_s();
  351. break;
  352. case MESI_CC_M_DATA: // data forwarded by M state guy Will never happen?
  353. sendmsg(false, MESI_CM_UNBLOCK_E);
  354. transition_to_m();
  355. break;
  356. //DEMAND_I could happen like this: The line was in S but was evicted. Manager was not notified. Later,
  357. //A request for the SAME line caused an I_to_E sent to manager. At the same time, the line was evicted
  358. //on manager side, so manager sends a DEMAND_I.
  359. case MESI_MC_DEMAND_I: //when a line X was evicted in the S state, manager still thinks it's a sharer;
  360. //a store for X would cause manager to invalidate X first (i.e., MC_DEMAND_I);
  361. //so we need to respond.
  362. sendmsg(false, MESI_CM_UNBLOCK_I);
  363. break;
  364. default:
  365. invalid_msg(msg);
  366. break;
  367. }
  368. }
  369. inline void MESI_client::do_SE(MESI_messages_t msg)
  370. {
  371. #ifdef DBG_MESI_CLIENT
  372. std::cout << "Client " << id << " in state SE, msg= " << msg << std::endl;
  373. #endif
  374. switch (msg)
  375. {
  376. case MESI_MC_GRANT_E_DATA: // data given to me by manager
  377. sendmsg(false, MESI_CM_UNBLOCK_E);
  378. transition_to_e();
  379. break;
  380. case MESI_MC_DEMAND_I: //while client changing from S to E, manager is evicting the same line.
  381. //The client's write request will be processed in due time, so we respond
  382. //to allow manager to finish eviciton first.
  383. sendmsg(false, MESI_CM_UNBLOCK_I);
  384. transition_to_ie();
  385. break;
  386. case MESI_CC_M_DATA: // This happens during a race condition. While we are going from S to E, another
  387. // client writes to the same line and its request got to the manager before us.
  388. // Finally when our I_to_E is being processed, the other client already is the owner,
  389. // so it will send us CC_M_DATA.
  390. sendmsg(false, MESI_CM_UNBLOCK_E);
  391. transition_to_m();
  392. break;
  393. default:
  394. invalid_msg(msg);
  395. break;
  396. }
  397. }
  398. inline void MESI_client::do_EI(MESI_messages_t msg, int fwdID)
  399. {
  400. #ifdef DBG_MESI_CLIENT
  401. std::cout << "Client in state EI, msg= " << msg << std::endl;
  402. #endif
  403. switch (msg)
  404. {
  405. case MESI_MC_GRANT_I:
  406. sendmsg(false, MESI_CM_UNBLOCK_I);
  407. transition_to_i();
  408. break;
  409. case MESI_MC_FWD_S: //we have started eviction, but manager just got a LOAD request and wants us to
  410. //transit to S state. We act as if the eviction never happened and we were still in E,
  411. //but silenly transit to I.
  412. //Manager would ignore the CM_E_to_I we just sent.
  413. sendmsg(false, MESI_CC_E_DATA, fwdID); // send to peer in IE. Send CC_E_DATA so it would enter E state.
  414. //sendmsg(false, MESI_CM_CLEAN); NO msg to manager since it will get UNBLOCK_E from peer.
  415. transition_to_i();
  416. break;
  417. case MESI_MC_FWD_E: //we have started eviction, but manager just got a STORE request and wants us to
  418. //transit to I state. We act as if the eviction never happened and we were still in E,
  419. //and silently transit to I.
  420. //Manager would ignore the CM_E_to_I we just sent.
  421. sendmsg(false, MESI_CC_E_DATA, fwdID);
  422. transition_to_i();
  423. break;
  424. case MESI_MC_DEMAND_I: //we have started eviction, but manager just got a request and wants to invalidate us.
  425. //We act as if the eviction never happened and we were still in E,
  426. //and silently transit to I.
  427. //Manager would ignore the CM_E_to_I we just sent.
  428. sendmsg(false, MESI_CM_UNBLOCK_I);
  429. transition_to_i();
  430. break;
  431. default:
  432. invalid_msg(msg);
  433. break;
  434. }
  435. }
  436. inline void MESI_client::do_MI(MESI_messages_t msg, int fwdID)
  437. {
  438. #ifdef DBG_MESI_CLIENT
  439. std::cout << "Client in state MI, msg= " << msg << std::endl;
  440. #endif
  441. switch (msg)
  442. {
  443. case MESI_MC_GRANT_I:
  444. sendmsg(false, MESI_CM_UNBLOCK_I);
  445. transition_to_i();
  446. break;
  447. case MESI_MC_FWD_S: //we have started eviction, but manager just got a LOAD request and wants us to
  448. //transit to S state. We act as if the eviction never happened and we were still in M,
  449. //but silenly transit to I.
  450. //Manager would ignore the CM_M_to_I we just sent.
  451. sendmsg(false, MESI_CC_M_DATA, fwdID); // send MESI_CC_M_DATA so requestor would enter M state.
  452. //sendmsg(false, MESI_CM_WRITEBACK); No msg to manager since it will get UNBLOCK_E from requestor.
  453. transition_to_i();
  454. break;
  455. case MESI_MC_FWD_E: //we have started eviction, but manager just got a STORE request and wants us to
  456. //transit to I state. We act as if the eviction never happened and we were still in M,
  457. //and silently transit to I.
  458. //Manager would ignore the CM_M_to_I we just sent.
  459. sendmsg(false, MESI_CC_M_DATA, fwdID);
  460. transition_to_i(); //go to I as if we were evicting in S state.
  461. break;
  462. case MESI_MC_DEMAND_I: //we have started eviction, but manager just got a request and wants to invalidate us.
  463. //We act as if the eviction never happened and we were still in M,
  464. //and silently transit to I.
  465. //Manager would ignore the CM_M_to_I we just sent.
  466. sendmsg(false, MESI_CM_UNBLOCK_I_DIRTY);
  467. transition_to_i();
  468. break;
  469. default:
  470. invalid_msg(msg);
  471. break;
  472. }
  473. }
  474. /*
  475. inline void MESI_client::do_SIE(MESI_messages_t msg)
  476. {
  477. #ifdef DBG_MESI_CLIENT
  478. std::cout << "Client in state SIE, msg= " << msg << std::endl;
  479. #endif
  480. switch (msg)
  481. {
  482. case MESI_MC_DEMAND_I:
  483. sendmsg(false, MESI_CM_UNBLOCK_I);
  484. transition_to_ie();
  485. break;
  486. default:
  487. invalid_msg(msg);
  488. break;
  489. }
  490. }
  491. */
  492. void MESI_client::transition_to_i()
  493. {
  494. state = MESI_C_I;
  495. invalidate();
  496. }
  497. void MESI_client::transition_to_ie() { state = MESI_C_IE; }
  498. void MESI_client::transition_to_se() { state = MESI_C_SE; }
  499. void MESI_client::transition_to_s() {state = MESI_C_S;}
  500. void MESI_client::transition_to_e() {state = MESI_C_E;}
  501. void MESI_client::transition_to_ei() {state = MESI_C_EI;}
  502. void MESI_client::transition_to_m() {state = MESI_C_M;}
  503. void MESI_client::transition_to_mi() {state = MESI_C_MI;}
  504. //void MESI_client::transition_to_sie() {state = MESI_C_SIE;}
  505. void MESI_client::invalid_msg(MESI_messages_t msg) {printf("Invalid message %d for state %d\n", msg, state); assert(0);}
  506. void MESI_client::invalid_state() {printf("Invalid state %d", state);}
  507. /*
  508. bool MESI_client :: invalidated()
  509. {
  510. return state == MESI_C_I;
  511. }
  512. */
  513. } //namespace mcp_cache_namespace
  514. } //namespace manifold