ZC API: Add checks for ensuring that regMode and deregMode are valid
[charm.git] / src / ck-core / ckrdma.h
1 /*
2  * Charm Onesided API Utility Functions
3  */
4
5 #ifndef _CKRDMA_H_
6 #define _CKRDMA_H_
7
8 #include "envelope.h"
9
10 /*********************************** Zerocopy Direct API **********************************/
11
12 #define CK_BUFFER_REG     CMK_BUFFER_REG
13 #define CK_BUFFER_UNREG   CMK_BUFFER_UNREG
14 #define CK_BUFFER_PREREG  CMK_BUFFER_PREREG
15 #define CK_BUFFER_NOREG   CMK_BUFFER_NOREG
16
17 #define CK_BUFFER_DEREG     CMK_BUFFER_DEREG
18 #define CK_BUFFER_NODEREG   CMK_BUFFER_NODEREG
19
20 #ifndef CMK_NOCOPY_DIRECT_BYTES
21
22 #if defined(_WIN32)
23 #define CMK_NOCOPY_DIRECT_BYTES 1
24 /* It is required to declare CMK_NOCOPY_DIRECT_BYTES to 1 instead of 0
25  * as this avoids the C2229 error (illegal zero-sized array)
26  * for char layerInfo[CMK_NOCOPY_DIRECT_BYTES] which is seen for
27  * a 0 sized array on VC++
28  */
29 #else
30 #define CMK_NOCOPY_DIRECT_BYTES 0
31 #endif // end of if defined(_WIN32)
32
33 #endif // end of ifndef CMK_NOCOPY_DIRECT_BYTES
34
35 #ifndef CMK_COMMON_NOCOPY_DIRECT_BYTES
36 #define CMK_COMMON_NOCOPY_DIRECT_BYTES 0
37 #endif
38
39 #define CkRdmaAlloc CmiRdmaAlloc
40 #define CkRdmaFree  CmiRdmaFree
41
42 // Represents the mode of the zerocopy transfer
43 // CkNcpyMode::MEMCPY indicates that the PEs are on the logical node and memcpy can be used
44 // CkNcpyMode::CMA indicates that the PEs are on the same physical node and CMA can be used
45 // CkNcpyMode::RDMA indicates that the neither MEMCPY or CMA can be used and REMOTE Direct Memory Access needs to be used
46 enum class CkNcpyMode : char { MEMCPY, CMA, RDMA };
47
48 // Represents the completion status of the zerocopy transfer (used as a return value for CkNcpyBuffer::get & CkNcpyBuffer:::put)
49 // CMA and MEMCPY transfers complete instantly and return CkNcpyStatus::complete
50 // RDMA transfers use a remote asynchronous call and hence return CkNcpyStatus::incomplete
51 enum class CkNcpyStatus : char { incomplete, complete };
52
53 // P2P_SEND mode is used for EM P2P Send API
54 // BCAST_SEND mode is used for EM BCAST Send API
55 // P2P_RECV mode is used for EM P2P Recv API
56 // BCAST_RECV mode is used for EM BCAST Send API
57 enum class ncpyEmApiMode : char { P2P_SEND, BCAST_SEND, P2P_RECV, BCAST_RECV };
58
59 // Struct passed in a ZC Post Entry Method to allow receiver side to post 
60 struct CkNcpyBufferPost {
61   // regMode
62   unsigned short int regMode;
63
64   // deregMode
65   unsigned short int deregMode;
66 };
67
68 // Class to represent an Zerocopy buffer
69 // CkSendBuffer(....) passed by the user internally translates to a CkNcpyBuffer
70 class CkNcpyBuffer{
71
72   private:
73
74   // bool to indicate registration for current values of ptr and cnt on pe
75   bool isRegistered;
76
77   // machine specific information about the buffer
78   #ifdef __GNUC__
79   #pragma GCC diagnostic push
80   #pragma GCC diagnostic ignored "-Wpedantic"
81   #endif
82   char layerInfo[CMK_COMMON_NOCOPY_DIRECT_BYTES + CMK_NOCOPY_DIRECT_BYTES];
83   #ifdef __GNUC__
84   #pragma GCC diagnostic pop
85   #endif
86
87 #if CMK_ERROR_CHECKING
88   void checkRegModeIsValid() {
89     if(regMode < CK_BUFFER_REG || regMode > CK_BUFFER_NOREG) CmiAbort("checkRegModeIsValid: Invalid value for regMode!\n");
90   }
91
92   void checkDeregModeIsValid() {
93     if(deregMode < CK_BUFFER_DEREG || deregMode > CK_BUFFER_NODEREG) CmiAbort("checkDeregModeIsValid: Invalid value for deregMode!\n");
94   }
95 #endif
96
97   public:
98   // pointer to the buffer
99   const void *ptr;
100
101   // number of bytes
102   size_t cnt;
103
104   // callback to be invoked on the sender/receiver
105   CkCallback cb;
106
107   // home pe
108   int pe;
109
110   // regMode
111   unsigned short int regMode;
112
113   // deregMode
114   unsigned short int deregMode;
115
116   // reference pointer
117   const void *ref;
118
119   // bcast ack handling pointer
120   const void *bcastAckInfo;
121
122   CkNcpyBuffer() : isRegistered(false), ptr(NULL), cnt(0), pe(-1), regMode(CK_BUFFER_REG), deregMode(CK_BUFFER_DEREG), ref(NULL), bcastAckInfo(NULL) {}
123
124   explicit CkNcpyBuffer(const void *ptr_, size_t cnt_, unsigned short int regMode_=CK_BUFFER_REG, unsigned short int deregMode_=CK_BUFFER_DEREG) {
125     cb = CkCallback(CkCallback::ignore);
126     init(ptr_, cnt_, regMode_, deregMode_);
127   }
128
129   explicit CkNcpyBuffer(const void *ptr_, size_t cnt_, CkCallback &cb_, unsigned short int regMode_=CK_BUFFER_REG, unsigned short int deregMode_=CK_BUFFER_DEREG) {
130     init(ptr_, cnt_, cb_, regMode_, deregMode_);
131   }
132
133   void print() {
134     CkPrintf("[%d][%d][%d] CkNcpyBuffer print: ptr:%p, size:%d, pe:%d, regMode=%d, deregMode=%d, ref:%p, bcastAckInfo:%p\n", CmiMyPe(), CmiMyNode(), CmiMyRank(), ptr, cnt, pe, regMode, deregMode, ref, bcastAckInfo);
135   }
136
137   void init(const void *ptr_, size_t cnt_, CkCallback &cb_, unsigned short int regMode_=CK_BUFFER_REG, unsigned short int deregMode_=CK_BUFFER_DEREG) {
138     cb   = cb_;
139     init(ptr_, cnt_, regMode_, deregMode_);
140   }
141
142   void init(const void *ptr_, size_t cnt_, unsigned short int regMode_=CK_BUFFER_REG, unsigned short int deregMode_=CK_BUFFER_DEREG) {
143     ptr  = ptr_;
144     cnt  = cnt_;
145     pe   = CkMyPe();
146     regMode = regMode_;
147     deregMode = deregMode_;
148
149     isRegistered = false;
150
151 #if CMK_ERROR_CHECKING
152     // Ensure that regMode is valid
153     checkRegModeIsValid();
154
155     // Ensure that deregMode is valid
156     checkDeregModeIsValid();
157 #endif
158
159     // Register memory everytime new values are initialized
160     if(cnt > 0)
161       registerMem();
162   }
163
164   void setRef(const void *ref_) {
165     ref = ref_;
166   }
167
168   const void *getRef() {
169     return ref;
170   }
171
172   // Register(Pin) the memory for the buffer
173   void registerMem()
174   {
175     // Check that this object is local when registerMem is called
176     CkAssert(CkNodeOf(pe) == CkMyNode());
177
178     // Set machine layer information when regMode is not CK_BUFFER_NOREG
179     if(regMode != CK_BUFFER_NOREG) {
180
181       CmiSetRdmaCommonInfo(&layerInfo[0], ptr, cnt);
182
183       /* Set the pointer layerInfo unconditionally for layers that don't require pinning (MPI, PAMI)
184        * or if regMode is REG, PREREG on layers that require pinning (GNI, Verbs, OFI) */
185 #if CMK_REG_REQUIRED
186       if(regMode == CK_BUFFER_REG || regMode == CK_BUFFER_PREREG)
187 #endif
188       {
189         CmiSetRdmaBufferInfo(layerInfo + CmiGetRdmaCommonInfoSize(), ptr, cnt, regMode);
190         isRegistered = true;
191       }
192     }
193   }
194
195   void setMode(unsigned short int regMode_) { regMode = regMode_; }
196
197   void memcpyGet(CkNcpyBuffer &source);
198   void memcpyPut(CkNcpyBuffer &destination);
199
200 #if CMK_USE_CMA
201   void cmaGet(CkNcpyBuffer &source);
202   void cmaPut(CkNcpyBuffer &destination);
203 #endif
204
205   void rdmaGet(CkNcpyBuffer &source);
206   void rdmaPut(CkNcpyBuffer &destination);
207
208   CkNcpyStatus get(CkNcpyBuffer &source);
209   CkNcpyStatus put(CkNcpyBuffer &destination);
210
211   // Deregister(Unpin) the memory that is registered for the buffer
212   void deregisterMem() {
213     // Check that this object is local when deregisterMem is called
214     CkAssert(CkNodeOf(pe) == CkMyNode());
215
216     if(isRegistered == false)
217       return;
218
219 #if CMK_REG_REQUIRED
220     if(regMode != CK_BUFFER_NOREG) {
221       CmiDeregisterMem(ptr, layerInfo + CmiGetRdmaCommonInfoSize(), pe, regMode);
222       isRegistered = false;
223     }
224 #endif
225   }
226
227   void pup(PUP::er &p) {
228     p((char *)&ptr, sizeof(ptr));
229     p((char *)&ref, sizeof(ref));
230     p((char *)&bcastAckInfo, sizeof(bcastAckInfo));
231     p|cnt;
232     p|cb;
233     p|pe;
234     p|regMode;
235     p|deregMode;
236     p|isRegistered;
237     PUParray(p, layerInfo, CMK_COMMON_NOCOPY_DIRECT_BYTES + CMK_NOCOPY_DIRECT_BYTES);
238   }
239
240   friend void CkRdmaDirectAckHandler(void *ack);
241
242   friend void CkRdmaEMBcastAckHandler(void *ack);
243
244   friend void constructSourceBufferObject(NcpyOperationInfo *info, CkNcpyBuffer &src);
245   friend void constructDestinationBufferObject(NcpyOperationInfo *info, CkNcpyBuffer &dest);
246
247   friend envelope* CkRdmaIssueRgets(envelope *env, ncpyEmApiMode emMode, void *forwardMsg);
248   friend void CkRdmaIssueRgets(envelope *env, ncpyEmApiMode emMode, void *forwardMsg, int numops, void **arrPtrs, CkNcpyBufferPost *postStructs);
249
250   friend void readonlyGet(CkNcpyBuffer &src, CkNcpyBuffer &dest, void *refPtr);
251   friend void readonlyCreateOnSource(CkNcpyBuffer &src);
252
253
254   friend void performEmApiNcpyTransfer(CkNcpyBuffer &source, CkNcpyBuffer &dest, int opIndex, int child_count, char *ref, int extraSize, CkNcpyMode ncpyMode, ncpyEmApiMode emMode);
255
256   friend void performEmApiRget(CkNcpyBuffer &source, CkNcpyBuffer &dest, int opIndex, char *ref, int extraSize, ncpyEmApiMode emMode);
257
258   friend void performEmApiCmaTransfer(CkNcpyBuffer &source, CkNcpyBuffer &dest, int child_count, ncpyEmApiMode emMode);
259
260   friend void deregisterMemFromMsg(envelope *env, bool isRecv);
261 };
262
263 // Ack handler for the Zerocopy Direct API
264 // Invoked on the completion of any RDMA operation calling using the Direct API
265 void CkRdmaDirectAckHandler(void *ack);
266
267 // Method to invoke a callback on a particular pe with a CkNcpyBuffer being passed
268 // as a part of a CkDataMsg. This method is used to invoke callbacks on specific pes
269 // after the completion of the Zerocopy Direct API operation
270 void invokeCallback(void *cb, int pe, CkNcpyBuffer &buff);
271
272 // Returns CkNcpyMode::MEMCPY if both the PEs are the same and memcpy can be used
273 // Returns CkNcpyMode::CMA if both the PEs are in the same physical node and CMA can be used
274 // Returns CkNcpyMode::RDMA if RDMA needs to be used
275 CkNcpyMode findTransferMode(int srcPe, int destPe);
276
277 void invokeSourceCallback(NcpyOperationInfo *info);
278
279 void invokeDestinationCallback(NcpyOperationInfo *info);
280
281 // Method to enqueue a message after the completion of an payload transfer
282 void enqueueNcpyMessage(int destPe, void *msg);
283
284 /*********************************** Zerocopy Entry Method API ****************************/
285 static inline CkNcpyBuffer CkSendBuffer(const void *ptr_, CkCallback &cb_, unsigned short int regMode_=CK_BUFFER_REG, unsigned short int deregMode_=CK_BUFFER_DEREG) {
286   return CkNcpyBuffer(ptr_, 0, cb_, regMode_, deregMode_);
287 }
288
289 static inline CkNcpyBuffer CkSendBuffer(const void *ptr_, unsigned short int regMode_=CK_BUFFER_REG, unsigned short int deregMode_=CK_BUFFER_DEREG) {
290   return CkNcpyBuffer(ptr_, 0, regMode_, deregMode_);
291 }
292
293 #if CMK_ONESIDED_IMPL
294
295 // NOTE: Inside CkRdmaIssueRgets, a large message allocation is made consisting of space
296 // for the destination or receiver buffers and some additional information required for processing
297 // and acknowledgment handling. The space for additional information is typically equal to
298 // sizeof(NcpyEmInfo) + numops * sizeof(NcpyEmBufferInfo)
299
300 // This structure is used to store zerocopy information associated with an entry method
301 // invocation which uses the RDMA mode of transfer in Zerocopy Entry Method API.
302 // A variable of the structure stores the information in order to access it after the
303 // completion of the Rget operation (which is an asynchronous call) in order to invoke
304 // the entry method
305 struct NcpyEmInfo{
306   int numOps; // number of zerocopy operations i.e number of buffers sent using CkSendBuffer
307   int counter; // used for tracking the number of completed RDMA operations
308   int pe;
309   ncpyEmApiMode mode; // used to distinguish between p2p and bcast
310   void *msg; // pointer to the Charm++ message which will be enqueued after completion of all Rgets
311   void *forwardMsg; // used for the ncpy broadcast api
312 };
313
314
315 // This structure is used to store the buffer information specific to each buffer being sent
316 // using the Zerocopy Entry Method API. A variable of the structure stores the information associated
317 // with each buffer
318 struct NcpyEmBufferInfo{
319   int index;  // Represents the index of the buffer information (from 0,1... numops - 1)
320   NcpyOperationInfo ncpyOpInfo; // Stores all the information required for the zerocopy operation
321 };
322
323
324 /*
325  * Extract ncpy buffer information from the metadata message,
326  * allocate buffers and issue ncpy calls (either memcpy or cma read or rdma get)
327  */
328 envelope* CkRdmaIssueRgets(envelope *env, ncpyEmApiMode emMode, void *forwardMsg = NULL);
329
330 void CkRdmaIssueRgets(envelope *env, ncpyEmApiMode emMode, void *forwardMsg, int numops, void **arrPtrs, CkNcpyBufferPost *postStructs);
331
332 void handleEntryMethodApiCompletion(NcpyOperationInfo *info);
333
334 void handleReverseEntryMethodApiCompletion(NcpyOperationInfo *info);
335
336 // Method called to pack rdma pointers
337 void CkPackRdmaPtrs(char *msgBuf);
338
339 // Method called to pack rdma pointers
340 void CkUnpackRdmaPtrs(char *msgBuf);
341
342 // Determine the number of ncpy ops and the sum of the ncpy buffer sizes
343 // from the metadata message
344 void getRdmaNumopsAndBufsize(envelope *env, int &numops, int &bufsize);
345
346 // Ack handler function for the nocopy EM API
347 void CkRdmaEMAckHandler(int destPe, void *ack);
348
349 void CkRdmaEMBcastPostAckHandler(void *msg);
350
351 struct NcpyBcastRecvPeerAckInfo{
352 #if CMK_SMP
353   std::atomic<int> numPeers;
354 #else
355   int numPeers;
356 #endif
357   void *bcastAckInfo;
358   void *msg;
359   int peerParentPe;
360 #if CMK_SMP
361     int getNumPeers() const {
362        return numPeers.load(std::memory_order_acquire);
363     } 
364     void setNumPeers(int r) {
365        return numPeers.store(r, std::memory_order_release);
366     }
367     int incNumPeers() {
368         return numPeers.fetch_add(1, std::memory_order_release);
369     }
370     int decNumPeers() {
371          return numPeers.fetch_sub(1, std::memory_order_release);
372     }
373 #else
374     int getNumPeers() const { return numPeers; }
375     void setNumPeers(int r) { numPeers = r; }
376     int incNumPeers() { return numPeers++; }
377     int decNumPeers() { return numPeers--; }
378 #endif
379
380 };
381
382
383
384 /***************************** Zerocopy Bcast Entry Method API ****************************/
385 struct NcpyBcastAckInfo{
386   int numChildren;
387   int counter;
388   bool isRoot;
389   int pe;
390   int numops;
391 };
392
393 struct NcpyBcastRootAckInfo : public NcpyBcastAckInfo {
394   CkNcpyBuffer src[0];
395 };
396
397 struct NcpyBcastInterimAckInfo : public NcpyBcastAckInfo {
398   void *msg;
399
400   // for RECV
401   bool isRecv;
402   bool isArray;
403   void *parentBcastAckInfo;
404   int origPe;
405
406 };
407
408 // Method called on the bcast source to store some information for ack handling
409 void CkRdmaPrepareBcastMsg(envelope *env);
410
411 void CkReplaceSourcePtrsInBcastMsg(envelope *env, NcpyBcastInterimAckInfo *bcastAckInfo, int origPe);
412
413 // Method called to extract the parent bcastAckInfo from the received message for ack handling
414 const void *getParentBcastAckInfo(void *msg, int &srcPe);
415
416 // Allocate a NcpyBcastInterimAckInfo and return the pointer
417 NcpyBcastInterimAckInfo *allocateInterimNodeAckObj(envelope *myEnv, envelope *myChildEnv, int pe);
418
419 void forwardMessageToChildNodes(envelope *myChildrenMsg, UChar msgType);
420
421 void forwardMessageToPeerNodes(envelope *myMsg, UChar msgType);
422
423 void handleBcastEntryMethodApiCompletion(NcpyOperationInfo *info);
424
425 void handleBcastReverseEntryMethodApiCompletion(NcpyOperationInfo *info);
426
427 void deregisterMemFromMsg(envelope *env, bool isRecv);
428
429 void handleMsgUsingCMAPostCompletionForSendBcast(envelope *copyenv, envelope *env, CkNcpyBuffer &source);
430
431 void processBcastSendEmApiCompletion(NcpyEmInfo *ncpyEmInfo, int destPe);
432
433 // Method called on intermediate nodes after RGET to switch old source pointers with my pointers
434 void CkReplaceSourcePtrsInBcastMsg(envelope *prevEnv, envelope *env, void *bcastAckInfo, int origPe);
435
436 void processBcastRecvEmApiCompletion(NcpyEmInfo *ncpyEmInfo, int destPe);
437
438 // Method called on the root node and other intermediate parent nodes on completion of RGET through ZC Bcast
439 void CkRdmaEMBcastAckHandler(void *ack);
440
441 void handleMsgOnChildPostCompletionForRecvBcast(envelope *env);
442
443 void handleMsgOnInterimPostCompletionForRecvBcast(envelope *env, NcpyBcastInterimAckInfo *bcastAckInfo, int pe);
444
445
446
447 /***************************** Zerocopy Readonly Bcast Support ****************************/
448
449 /* Support for Zerocopy Broadcast of large readonly variables */
450 CkpvExtern(int, _numPendingRORdmaTransfers);
451
452 struct NcpyROBcastBuffAckInfo {
453   const void *ptr;
454
455   int regMode;
456
457   int pe;
458
459   // machine specific information about the buffer
460   #ifdef __GNUC__
461   #pragma GCC diagnostic push
462   #pragma GCC diagnostic ignored "-Wpedantic"
463   #endif
464   char layerInfo[CMK_COMMON_NOCOPY_DIRECT_BYTES + CMK_NOCOPY_DIRECT_BYTES];
465   #ifdef __GNUC__
466   #pragma GCC diagnostic pop
467   #endif
468 };
469
470 struct NcpyROBcastAckInfo {
471   int numChildren;
472   int counter;
473   bool isRoot;
474   int numops;
475   NcpyROBcastBuffAckInfo buffAckInfo[0];
476 };
477
478 void readonlyUpdateNumops();
479
480 void readonlyAllocateOnSource();
481
482 void readonlyCreateOnSource(CkNcpyBuffer &src);
483
484 void readonlyGet(CkNcpyBuffer &src, CkNcpyBuffer &dest, void *refPtr);
485
486 void readonlyGetCompleted(NcpyOperationInfo *ncpyOpInfo);
487
488 #if CMK_SMP
489 void updatePeerCounterAndPush(envelope *env);
490 #endif
491
492 CkArray* getArrayMgrFromMsg(envelope *env);
493
494 void sendAckMsgToParent(envelope *env);
495
496 void sendRecvDoneMsgToPeers(envelope *env, CkArray *mgr);
497
498 #endif /* End of CMK_ONESIDED_IMPL */
499
500 #endif