1 /*
2  * Copyright (c) 2019 Intel Corporation. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32 
33 #include <inttypes.h>
34 
35 #include "ofi.h"
36 #include "ofi_prov.h"
37 #include "ofi_hook.h"
38 #include "hook_prov.h"
39 #include "ofi_enosys.h"
40 
41 #include "hook_debug.h"
42 
43 struct hook_prov_ctx hook_debug_prov_ctx;
44 
45 struct hook_debug_config config = {
46 	.trace_exit = 1,
47 	.trace_cq_entry = 1,
48 	.track_sends = 1,
49 	/* Disable for now: debug hang */
50 	.track_recvs = 0,
51 };
52 
53 static struct hook_debug_txrx_entry *
hook_debug_get_tx_entry(struct hook_debug_ep * myep,void * context,uint64_t flags)54 hook_debug_get_tx_entry(struct hook_debug_ep *myep, void *context,
55 			uint64_t flags)
56 {
57 	struct hook_debug_txrx_entry *tx_entry;
58 
59 	tx_entry = ofi_buf_alloc(myep->tx_pool);
60 	assert(tx_entry);
61 	assert(tx_entry->magic == OFI_MAGIC_64);
62 
63 	tx_entry->op_flags = myep->tx_op_flags | flags;
64 	tx_entry->context = context;
65 	return tx_entry;
66 }
67 
68 static struct hook_debug_txrx_entry *
hook_debug_get_rx_entry(struct hook_debug_ep * myep,void * context,uint64_t flags)69 hook_debug_get_rx_entry(struct hook_debug_ep *myep, void *context,
70 			uint64_t flags)
71 {
72 	struct hook_debug_txrx_entry *rx_entry;
73 
74 	rx_entry = ofi_buf_alloc(myep->rx_pool);
75 	assert(rx_entry);
76 	assert(rx_entry->magic == OFI_MAGIC_64);
77 
78 	rx_entry->op_flags = myep->rx_op_flags | flags;
79 	rx_entry->context = context;
80 	return rx_entry;
81 }
82 
hook_debug_trace_exit(struct fid * fid,struct fid * hfid,enum fi_log_subsys subsys,const char * fn,ssize_t ret,size_t * eagain_count)83 static void hook_debug_trace_exit(struct fid *fid, struct fid *hfid,
84 				  enum fi_log_subsys subsys, const char *fn,
85 				  ssize_t ret, size_t *eagain_count)
86 {
87 	if (!config.trace_exit)
88 		return;
89 
90 	if (ret > 0) {
91 		FI_TRACE(hook_to_hprov(fid), subsys, "%s (fid: %p) returned: "
92 			 "%zd\n", fn, hfid, ret);
93 		goto out;
94 	}
95 
96 	if (ret != -FI_EAGAIN || !eagain_count ||
97 	    !((*eagain_count)++ % HOOK_DEBUG_EAGAIN_LOG))
98 		FI_TRACE(hook_to_hprov(fid), subsys, "%s (fid: %p) returned: "
99 			 "%zd (%s)\n", fn, hfid, ret, fi_strerror(-ret));
100 out:
101 	if (eagain_count && ret != -FI_EAGAIN)
102 		*eagain_count = 0;
103 }
104 
105 static void
hook_debug_trace_exit_eq(struct hook_debug_eq * eq,const char * fn,ssize_t ret)106 hook_debug_trace_exit_eq(struct hook_debug_eq *eq, const char *fn, ssize_t ret)
107 {
108 	return hook_debug_trace_exit(&eq->hook_eq.eq.fid, &eq->hook_eq.heq->fid,
109 				     FI_LOG_EQ, fn, ret, &eq->eagain_count);
110 }
111 
112 static void
hook_debug_trace_exit_cq(struct hook_debug_cq * cq,const char * fn,ssize_t ret)113 hook_debug_trace_exit_cq(struct hook_debug_cq *cq, const char *fn, ssize_t ret)
114 {
115 	hook_debug_trace_exit(&cq->hook_cq.cq.fid, &cq->hook_cq.hcq->fid,
116 			      FI_LOG_CQ, fn, ret, &cq->eagain_count);
117 }
118 
119 static void
hook_debug_trace_exit_cntr(struct hook_cntr * cntr,const char * fn,ssize_t ret)120 hook_debug_trace_exit_cntr(struct hook_cntr *cntr, const char *fn, ssize_t ret)
121 {
122 	hook_debug_trace_exit(&cntr->cntr.fid, &cntr->hcntr->fid,
123 			      FI_LOG_CNTR, fn, ret, NULL);
124 }
125 
126 static void
hook_debug_trace_exit_ep(struct hook_debug_ep * ep,const char * fn,ssize_t ret,size_t * eagain_count)127 hook_debug_trace_exit_ep(struct hook_debug_ep *ep, const char *fn, ssize_t ret,
128 			 size_t *eagain_count)
129 {
130 	hook_debug_trace_exit(&ep->hook_ep.ep.fid, &ep->hook_ep.hep->fid,
131 			      FI_LOG_EP_DATA, fn, ret, eagain_count);
132 }
133 
hook_debug_rx_end(struct hook_debug_ep * ep,char * fn,ssize_t ret,void * mycontext)134 static void hook_debug_rx_end(struct hook_debug_ep *ep, char *fn,
135 			      ssize_t ret, void *mycontext)
136 {
137 	struct hook_debug_txrx_entry *rx_entry;
138 
139 	hook_debug_trace_exit_ep(ep, fn, ret, &ep->rx_eagain_count);
140 
141 	if (config.track_recvs) {
142 		if (!ret) {
143 			ep->rx_outs++;
144 			FI_TRACE(hook_to_hprov(&ep->hook_ep.ep.fid),
145 				 FI_LOG_EP_DATA, "ep: %p rx_outs: %zu\n",
146 				 ep->hook_ep.hep, ep->rx_outs);
147 		} else {
148 			rx_entry = mycontext;
149 			ofi_buf_free(rx_entry);
150 		}
151 	}
152 }
153 
hook_debug_rx_start(struct hook_debug_ep * ep,void * context,uint64_t flags,void ** mycontext)154 static int hook_debug_rx_start(struct hook_debug_ep *ep, void *context,
155 			       uint64_t flags, void **mycontext)
156 {
157 	struct hook_debug_txrx_entry *rx_entry;
158 
159 	if (config.track_recvs) {
160 		if (flags & ~(FI_MULTI_RECV | FI_COMPLETION)) {
161 			FI_TRACE(&hook_debug_prov_ctx.prov, FI_LOG_EP_DATA,
162 				 "unsupported flags: %s\n",
163 				 fi_tostr(&flags, FI_TYPE_OP_FLAGS));
164 			return -FI_EINVAL;
165 		}
166 
167 		rx_entry = hook_debug_get_rx_entry(ep, context, flags);
168 		*mycontext = rx_entry;
169 	} else {
170 		*mycontext = context;
171 	}
172 	return 0;
173 }
174 
175 static ssize_t
hook_debug_recv(struct fid_ep * ep,void * buf,size_t len,void * desc,fi_addr_t src_addr,void * context)176 hook_debug_recv(struct fid_ep *ep, void *buf, size_t len, void *desc,
177 		fi_addr_t src_addr, void *context)
178 {
179 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep,
180 						  hook_ep.ep);
181 	void *mycontext;
182 	ssize_t ret;
183 
184 	ret = hook_debug_rx_start(myep, context, 0, &mycontext);
185 	if (ret)
186 		return ret;
187 
188 	ret = fi_recv(myep->hook_ep.hep, buf, len, desc, src_addr, mycontext);
189 	hook_debug_rx_end(myep, "fi_recv", ret, mycontext);
190 	return ret;
191 }
192 
193 static ssize_t
hook_debug_recvmsg(struct fid_ep * ep,const struct fi_msg * msg,uint64_t flags)194 hook_debug_recvmsg(struct fid_ep *ep, const struct fi_msg *msg, uint64_t flags)
195 {
196 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
197 	struct fi_msg mymsg = *msg;
198 	ssize_t ret;
199 
200 	ret = hook_debug_rx_start(myep, msg->context, flags, &mymsg.context);
201 	if (ret)
202 		return ret;
203 
204 	ret = fi_recvmsg(myep->hook_ep.hep, &mymsg, flags);
205 	hook_debug_rx_end(myep, "fi_recvmsg", ret, mymsg.context);
206 	return ret;
207 }
208 
209 static ssize_t
hook_debug_trecv(struct fid_ep * ep,void * buf,size_t len,void * desc,fi_addr_t src_addr,uint64_t tag,uint64_t ignore,void * context)210 hook_debug_trecv(struct fid_ep *ep, void *buf, size_t len, void *desc,
211 		 fi_addr_t src_addr, uint64_t tag, uint64_t ignore,
212 		 void *context)
213 {
214 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
215 	void *mycontext;
216 	ssize_t ret;
217 
218 	ret = hook_debug_rx_start(myep, context, 0, &mycontext);
219 	if (ret)
220 		return ret;
221 
222 	ret = fi_trecv(myep->hook_ep.hep, buf, len, desc, src_addr,
223 		       tag, ignore, mycontext);
224 
225 	hook_debug_rx_end(myep, "fi_trecv", ret, mycontext);
226 	return ret;
227 }
228 
hook_debug_tx_end(struct hook_debug_ep * ep,char * fn,ssize_t ret,void * mycontext)229 static void hook_debug_tx_end(struct hook_debug_ep *ep, char *fn,
230 			      ssize_t ret, void *mycontext)
231 {
232 	struct hook_debug_txrx_entry *tx_entry;
233 
234 	hook_debug_trace_exit_ep(ep, fn, ret, &ep->tx_eagain_count);
235 
236 	if (mycontext && config.track_sends) {
237 		if (!ret) {
238 			ep->tx_outs++;
239 			FI_TRACE(hook_to_hprov(&ep->hook_ep.ep.fid),
240 				 FI_LOG_EP_DATA, "ep: %p tx_outs: %zu\n",
241 				 ep->hook_ep.hep, ep->tx_outs);
242 		} else {
243 			tx_entry = mycontext;
244 			ofi_buf_free(tx_entry);
245 		}
246 	}
247 }
248 
hook_debug_tx_start(struct hook_debug_ep * ep,void * context,uint64_t flags,void ** mycontext)249 static int hook_debug_tx_start(struct hook_debug_ep *ep, void *context,
250 			       uint64_t flags, void **mycontext)
251 {
252 	struct hook_debug_txrx_entry *tx_entry;
253 
254 	if (mycontext) {
255 		if (config.track_sends) {
256 			tx_entry = hook_debug_get_tx_entry(ep, context, flags);
257 			*mycontext = tx_entry;
258 		} else {
259 			*mycontext = context;
260 		}
261 	}
262 	return 0;
263 }
264 
265 static ssize_t
hook_debug_send(struct fid_ep * ep,const void * buf,size_t len,void * desc,fi_addr_t dest_addr,void * context)266 hook_debug_send(struct fid_ep *ep, const void *buf, size_t len, void *desc,
267 	      fi_addr_t dest_addr, void *context)
268 {
269 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
270 	void *mycontext;
271 	ssize_t ret;
272 
273 	ret = hook_debug_tx_start(myep, context, 0, &mycontext);
274 	if (ret)
275 		return ret;
276 
277 	ret = fi_send(myep->hook_ep.hep, buf, len, desc, dest_addr, mycontext);
278 
279 	hook_debug_tx_end(myep, "fi_send", ret, mycontext);
280 	return ret;
281 }
282 
283 static ssize_t
hook_debug_sendv(struct fid_ep * ep,const struct iovec * iov,void ** desc,size_t count,fi_addr_t dest_addr,void * context)284 hook_debug_sendv(struct fid_ep *ep, const struct iovec *iov, void **desc,
285 		 size_t count, fi_addr_t dest_addr, void *context)
286 {
287 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
288 	void *mycontext;
289 	ssize_t ret;
290 
291 	ret = hook_debug_tx_start(myep, context, 0, &mycontext);
292 	if (ret)
293 		return ret;
294 
295 	ret = fi_sendv(myep->hook_ep.hep, iov, desc, count, dest_addr, mycontext);
296 
297 	hook_debug_tx_end(myep, "fi_sendv", ret, mycontext);
298 	return ret;
299 }
300 
301 static ssize_t
hook_debug_sendmsg(struct fid_ep * ep,const struct fi_msg * msg,uint64_t flags)302 hook_debug_sendmsg(struct fid_ep *ep, const struct fi_msg *msg, uint64_t flags)
303 {
304 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
305 	struct fi_msg mymsg = *msg;
306 	ssize_t ret;
307 
308 	ret = hook_debug_tx_start(myep, msg->context, flags, &mymsg.context);
309 	if (ret)
310 		return ret;
311 
312 	ret = fi_sendmsg(myep->hook_ep.hep, &mymsg, flags);
313 	hook_debug_tx_end(myep, "fi_sendmsg", ret, mymsg.context);
314 	return ret;
315 }
316 
317 static ssize_t
hook_debug_inject(struct fid_ep * ep,const void * buf,size_t len,fi_addr_t dest_addr)318 hook_debug_inject(struct fid_ep *ep, const void *buf, size_t len,
319 		fi_addr_t dest_addr)
320 {
321 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
322 	ssize_t ret;
323 
324 	ret = hook_debug_tx_start(myep, NULL, 0, NULL);
325 	if (ret)
326 		return ret;
327 
328 	ret = fi_inject(myep->hook_ep.hep, buf, len, dest_addr);
329 
330 	hook_debug_tx_end(myep, "fi_inject", ret, NULL);
331 	return ret;
332 }
333 
334 static ssize_t
hook_debug_senddata(struct fid_ep * ep,const void * buf,size_t len,void * desc,uint64_t data,fi_addr_t dest_addr,void * context)335 hook_debug_senddata(struct fid_ep *ep, const void *buf, size_t len, void *desc,
336 		  uint64_t data, fi_addr_t dest_addr, void *context)
337 {
338 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
339 	void *mycontext;
340 	ssize_t ret;
341 
342 	ret = hook_debug_tx_start(myep, context, 0, &mycontext);
343 	if (ret)
344 		return ret;
345 
346 	ret = fi_senddata(myep->hook_ep.hep, buf, len, desc, data, dest_addr, mycontext);
347 	hook_debug_tx_end(myep, "fi_senddata", ret, mycontext);
348 	return ret;
349 }
350 
351 static ssize_t
hook_debug_injectdata(struct fid_ep * ep,const void * buf,size_t len,uint64_t data,fi_addr_t dest_addr)352 hook_debug_injectdata(struct fid_ep *ep, const void *buf, size_t len,
353 		      uint64_t data, fi_addr_t dest_addr)
354 {
355 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
356 	ssize_t ret;
357 
358 	ret = hook_debug_tx_start(myep, NULL, 0, NULL);
359 	if (ret)
360 		return ret;
361 
362 	ret = fi_injectdata(myep->hook_ep.hep, buf, len, data, dest_addr);
363 
364 	hook_debug_tx_end(myep, "fi_injectdata", ret, NULL);
365 	return ret;
366 }
367 
368 static ssize_t
hook_debug_tsend(struct fid_ep * ep,const void * buf,size_t len,void * desc,fi_addr_t dest_addr,uint64_t tag,void * context)369 hook_debug_tsend(struct fid_ep *ep, const void *buf, size_t len, void *desc,
370 		 fi_addr_t dest_addr, uint64_t tag, void *context)
371 {
372 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
373 	void *mycontext;
374 	ssize_t ret;
375 
376 	ret = hook_debug_tx_start(myep, context, 0, &mycontext);
377 	if (ret)
378 		return ret;
379 
380 	ret = fi_tsend(myep->hook_ep.hep, buf, len, desc, dest_addr, tag, mycontext);
381 	hook_debug_tx_end(myep, "fi_tsend", ret, mycontext);
382 	return ret;
383 }
384 
385 static ssize_t
hook_debug_tsendv(struct fid_ep * ep,const struct iovec * iov,void ** desc,size_t count,fi_addr_t dest_addr,uint64_t tag,void * context)386 hook_debug_tsendv(struct fid_ep *ep, const struct iovec *iov, void **desc,
387 		  size_t count, fi_addr_t dest_addr, uint64_t tag, void *context)
388 {
389 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
390 	void *mycontext;
391 	ssize_t ret;
392 
393 	ret = hook_debug_tx_start(myep, context, 0, &mycontext);
394 	if (ret)
395 		return ret;
396 
397 	ret = fi_tsendv(myep->hook_ep.hep, iov, desc, count,
398 			dest_addr, tag, mycontext);
399 
400 	hook_debug_tx_end(myep, "fi_tsendv", ret, mycontext);
401 	return ret;
402 }
403 
404 static ssize_t
hook_debug_tsendmsg(struct fid_ep * ep,const struct fi_msg_tagged * msg,uint64_t flags)405 hook_debug_tsendmsg(struct fid_ep *ep, const struct fi_msg_tagged *msg,
406 		    uint64_t flags)
407 {
408 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
409 	struct fi_msg_tagged mymsg = *msg;
410 	ssize_t ret;
411 
412 	ret = hook_debug_tx_start(myep, msg->context, flags, &mymsg.context);
413 	if (ret)
414 		return ret;
415 
416 	ret = fi_tsendmsg(myep->hook_ep.hep, &mymsg, flags);
417 	hook_debug_tx_end(myep, "fi_tsendmsg", ret, mymsg.context);
418 	return ret;
419 }
420 
421 static ssize_t
hook_debug_tinject(struct fid_ep * ep,const void * buf,size_t len,fi_addr_t dest_addr,uint64_t tag)422 hook_debug_tinject(struct fid_ep *ep, const void *buf, size_t len,
423 		   fi_addr_t dest_addr, uint64_t tag)
424 {
425 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
426 	ssize_t ret;
427 
428 	ret = hook_debug_tx_start(myep, NULL, 0, NULL);
429 	if (ret)
430 		return ret;
431 
432 	ret = fi_tinject(myep->hook_ep.hep, buf, len, dest_addr, tag);
433 
434 	hook_debug_tx_end(myep, "fi_tinject", ret, NULL);
435 	return ret;
436 }
437 
438 static ssize_t
hook_debug_tsenddata(struct fid_ep * ep,const void * buf,size_t len,void * desc,uint64_t data,fi_addr_t dest_addr,uint64_t tag,void * context)439 hook_debug_tsenddata(struct fid_ep *ep, const void *buf, size_t len, void *desc,
440 		     uint64_t data, fi_addr_t dest_addr, uint64_t tag,
441 		     void *context)
442 {
443 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
444 	void *mycontext;
445 	ssize_t ret;
446 
447 	ret = hook_debug_tx_start(myep, context, 0, &mycontext);
448 	if (ret)
449 		return ret;
450 
451 	ret = fi_tsenddata(myep->hook_ep.hep, buf, len, desc, data,
452 			    dest_addr, tag, mycontext);
453 	hook_debug_tx_end(myep, "fi_tsenddata", ret, mycontext);
454 	return ret;
455 }
456 
457 static ssize_t
hook_debug_tinjectdata(struct fid_ep * ep,const void * buf,size_t len,uint64_t data,fi_addr_t dest_addr,uint64_t tag)458 hook_debug_tinjectdata(struct fid_ep *ep, const void *buf, size_t len,
459 		       uint64_t data, fi_addr_t dest_addr, uint64_t tag)
460 {
461 	struct hook_debug_ep *myep = container_of(ep, struct hook_debug_ep, hook_ep.ep);
462 	ssize_t ret;
463 
464 	ret = hook_debug_tx_start(myep, NULL, 0, NULL);
465 	if (ret)
466 		return ret;
467 
468 	ret = fi_tinjectdata(myep->hook_ep.hep, buf, len, data, dest_addr, tag);
469 
470 	hook_debug_tx_end(myep, "fi_tinjectdata", ret, NULL);
471 	return ret;
472 }
473 
474 #define HOOK_DEBUG_TRACE(fabric, subsys, ...) \
475 	FI_TRACE(hook_fabric_to_hprov(fabric), subsys, __VA_ARGS__)
476 
477 #define HOOK_DEBUG_CQ_TRACE(cq, ...) \
478 	HOOK_DEBUG_TRACE(cq->hook_cq.domain->fabric, FI_LOG_CQ, __VA_ARGS__)
479 
hook_debug_cq_entry_log(struct hook_debug_cq * cq,struct fi_cq_tagged_entry * entry)480 static void hook_debug_cq_entry_log(struct hook_debug_cq *cq,
481 				    struct fi_cq_tagged_entry *entry)
482 {
483 	if (!config.trace_cq_entry)
484 		return;
485 
486 	HOOK_DEBUG_CQ_TRACE(cq, "cq_entry:\n");
487 	HOOK_DEBUG_CQ_TRACE(cq, "\top_context: %p\n", entry->op_context);
488 
489 	if (cq->format > FI_CQ_FORMAT_CONTEXT) {
490 		HOOK_DEBUG_CQ_TRACE(cq, "\tflags: %s\n",
491 				    fi_tostr(&entry->flags, FI_TYPE_CAPS));
492 
493 		if (entry->flags & FI_RECV)
494 			HOOK_DEBUG_CQ_TRACE(cq, "\tlen: %zu\n", entry->len);
495 
496 		if (cq->format == FI_CQ_FORMAT_TAGGED)
497 			HOOK_DEBUG_CQ_TRACE(cq, "\ttag: %" PRIx64 "\n", entry->tag);
498 	}
499 }
500 
hook_debug_cq_process_entry(struct hook_debug_cq * mycq,const char * fn,ssize_t ret,char * buf)501 static void hook_debug_cq_process_entry(struct hook_debug_cq *mycq,
502 					const char *fn, ssize_t ret, char *buf)
503 {
504 	struct hook_debug_txrx_entry *rx_entry, *tx_entry;
505 	struct fi_cq_tagged_entry *cq_entry;
506 	int i;
507 
508 	hook_debug_trace_exit_cq(mycq, fn, ret);
509 
510 	for (i = 0; i < ret; i++, buf += mycq->entry_size) {
511 		cq_entry = (struct fi_cq_tagged_entry *)buf;
512 		hook_debug_cq_entry_log(mycq, cq_entry);
513 
514 		if (config.track_recvs && (cq_entry->flags & FI_RECV)) {
515 			rx_entry = cq_entry->op_context;
516 			assert(rx_entry->magic == OFI_MAGIC_64);
517 
518 			cq_entry->op_context = rx_entry->context;
519 
520 			if (!(rx_entry->op_flags & FI_MULTI_RECV) ||
521 			    cq_entry->flags & FI_MULTI_RECV) {
522 				rx_entry->ep->rx_outs--;
523 				FI_TRACE(hook_to_hprov(&mycq->hook_cq.cq.fid),
524 					 FI_LOG_CQ, "ep: %p rx_outs: %zu\n",
525 					 rx_entry->ep->hook_ep.hep,
526 					 rx_entry->ep->rx_outs);
527 				ofi_buf_free(rx_entry);
528 			}
529 		} else if (config.track_sends && (cq_entry->flags & FI_SEND)) {
530 			tx_entry = cq_entry->op_context;
531 			assert(tx_entry->magic == OFI_MAGIC_64);
532 
533 			cq_entry->op_context = tx_entry->context;
534 
535 			tx_entry->ep->tx_outs--;
536 			FI_TRACE(hook_to_hprov(&mycq->hook_cq.cq.fid),
537 				 FI_LOG_CQ, "ep: %p tx_outs: %zu\n",
538 				 tx_entry->ep->hook_ep.hep,
539 				 tx_entry->ep->tx_outs);
540 			ofi_buf_free(tx_entry);
541 		}
542 	}
543 }
544 
hook_debug_cq_read(struct fid_cq * cq,void * buf,size_t count)545 static ssize_t hook_debug_cq_read(struct fid_cq *cq, void *buf, size_t count)
546 {
547 	struct hook_debug_cq *mycq = container_of(cq, struct hook_debug_cq,
548 						  hook_cq.cq);
549 	ssize_t ret;
550 
551 	ret = fi_cq_read(mycq->hook_cq.hcq, buf, count);
552 	hook_debug_cq_process_entry(mycq, "fi_cq_read", ret, buf);
553 	return ret;
554 }
555 
hook_debug_cq_readfrom(struct fid_cq * cq,void * buf,size_t count,fi_addr_t * src_addr)556 static ssize_t hook_debug_cq_readfrom(struct fid_cq *cq, void *buf, size_t count,
557 				      fi_addr_t *src_addr)
558 {
559 	struct hook_debug_cq *mycq = container_of(cq, struct hook_debug_cq,
560 						  hook_cq.cq);
561 	ssize_t ret;
562 
563 	ret = fi_cq_readfrom(mycq->hook_cq.hcq, buf, count, src_addr);
564 	hook_debug_cq_process_entry(mycq, "fi_cq_readfrom", ret, buf);
565 	return ret;
566 }
567 
hook_debug_cq_close(struct fid * fid)568 int hook_debug_cq_close(struct fid *fid)
569 {
570 	struct hook_debug_cq *mycq =
571 		container_of(fid, struct hook_debug_cq, hook_cq.cq.fid);
572 	int ret = 0;
573 
574 	if (mycq->hook_cq.hcq)
575 		ret = fi_close(&mycq->hook_cq.hcq->fid);
576 	if (!ret)
577 		free(mycq);
578 	return ret;
579 }
580 
581 // TODO move to common code
582 static size_t cq_entry_size[] = {
583 	[FI_CQ_FORMAT_UNSPEC] = 0,
584 	[FI_CQ_FORMAT_CONTEXT] = sizeof(struct fi_cq_entry),
585 	[FI_CQ_FORMAT_MSG] = sizeof(struct fi_cq_msg_entry),
586 	[FI_CQ_FORMAT_DATA] = sizeof(struct fi_cq_data_entry),
587 	[FI_CQ_FORMAT_TAGGED] = sizeof(struct fi_cq_tagged_entry)
588 };
589 
590 struct fi_ops hook_debug_cq_fid_ops;
591 struct fi_ops_cq hook_debug_cq_ops;
592 
hook_debug_cq_attr_log(struct hook_domain * dom,struct fi_cq_attr * attr)593 static void hook_debug_cq_attr_log(struct hook_domain *dom,
594 				   struct fi_cq_attr *attr)
595 {
596 	HOOK_DEBUG_TRACE(dom->fabric, FI_LOG_CQ, "fi_cq_attr:\n");
597 	HOOK_DEBUG_TRACE(dom->fabric, FI_LOG_CQ, "\tsize: %zu\n", attr->size);
598 	HOOK_DEBUG_TRACE(dom->fabric, FI_LOG_CQ, "\tflags: %s\n", "TBD");
599 	HOOK_DEBUG_TRACE(dom->fabric, FI_LOG_CQ, "\tformat: %s\n", "TBD");
600 	HOOK_DEBUG_TRACE(dom->fabric, FI_LOG_CQ, "\twait_obj: %s\n", "TBD");
601 	HOOK_DEBUG_TRACE(dom->fabric, FI_LOG_CQ, "\tsignaling_vector: %d\n",
602 			 attr->signaling_vector);
603 	HOOK_DEBUG_TRACE(dom->fabric, FI_LOG_CQ, "\twait_cond: %s\n", "TBD");
604 	HOOK_DEBUG_TRACE(dom->fabric, FI_LOG_CQ, "\twait_set: %p\n", attr->wait_set);
605 }
606 
hook_debug_cq_open(struct fid_domain * domain_fid,struct fi_cq_attr * attr,struct fid_cq ** cq,void * context)607 int hook_debug_cq_open(struct fid_domain *domain_fid, struct fi_cq_attr *attr,
608 		       struct fid_cq **cq, void *context)
609 {
610 	struct hook_domain *domain = container_of(domain_fid, struct hook_domain,
611 						  domain);
612 	struct hook_debug_cq *mycq;
613 	int ret;
614 
615 	assert(!attr->flags);
616 
617 	hook_debug_cq_attr_log(domain, attr);
618 
619 	if ((config.track_sends || config.track_recvs) &&
620 	    (attr->format < FI_CQ_FORMAT_MSG)) {
621 		FI_WARN(&hook_debug_prov_ctx.prov, FI_LOG_CQ,
622 			"need FI_CQ_FORMAT_MSG or higher for tracking sends "
623 			"and(or) recvs\n");
624 		return -FI_EINVAL;
625 	}
626 
627 	mycq = calloc(1, sizeof *mycq);
628 	if (!mycq)
629 		return -FI_EAGAIN;
630 
631 	ret = hook_cq_init(domain_fid, attr, cq, context, &mycq->hook_cq);
632 	if (ret)
633 		goto err;
634 
635 	FI_TRACE(hook_fabric_to_hprov(mycq->hook_cq.domain->fabric), FI_LOG_CQ,
636 		 "cq opened, fid: %p\n", &mycq->hook_cq.hcq->fid);
637 
638 	mycq->hook_cq.cq.fid.ops = &hook_debug_cq_fid_ops;
639 	mycq->hook_cq.cq.ops = &hook_debug_cq_ops;
640 	mycq->format = attr->format;
641 	mycq->entry_size = cq_entry_size[attr->format];
642 
643 	assert(mycq->entry_size);
644 
645 	return 0;
646 err:
647 	hook_debug_cq_close(&mycq->hook_cq.cq.fid);
648 	return ret;
649 }
650 
hook_debug_ep_close(struct fid * fid)651 static int hook_debug_ep_close(struct fid *fid)
652 {
653 	struct hook_debug_ep *myep =
654 		container_of(fid, struct hook_debug_ep, hook_ep.ep.fid);
655 	int ret = 0;
656 
657 	if (myep->tx_pool)
658 		ofi_bufpool_destroy(myep->tx_pool);
659 
660 	if (myep->rx_pool)
661 		ofi_bufpool_destroy(myep->rx_pool);
662 
663 	if (myep->hook_ep.hep)
664 		ret = fi_close(&myep->hook_ep.hep->fid);
665 	if (!ret)
666 		free(myep);
667 	return ret;
668 }
669 
hook_debug_ep_bind(struct fid * fid,struct fid * bfid,uint64_t flags)670 int hook_debug_ep_bind(struct fid *fid, struct fid *bfid, uint64_t flags)
671 {
672 	struct fid *hfid, *hbfid;
673 	struct hook_cntr *cntr;
674 	struct hook_cq *cq;
675 
676 	hfid = hook_to_hfid(fid);
677 	hbfid = hook_to_hfid(bfid);
678 	if (!hfid || !hbfid)
679 		return -FI_EINVAL;
680 
681 	switch (bfid->fclass) {
682 	case FI_CLASS_CQ:
683 		cq = container_of(bfid, struct hook_cq, cq.fid);
684 		HOOK_DEBUG_TRACE(cq->domain->fabric, FI_LOG_EP_CTRL,
685 				 "cq: %p bind flags: %s\n", cq->hcq,
686 				 fi_tostr(&flags, FI_TYPE_CAPS));
687 		break;
688 	case FI_CLASS_CNTR:
689 		cntr = container_of(bfid, struct hook_cntr, cntr.fid);
690 		HOOK_DEBUG_TRACE(cntr->domain->fabric, FI_LOG_EP_CTRL,
691 				 "cntr: %p bind flags: %s\n", cntr->hcntr,
692 				 fi_tostr(&flags, FI_TYPE_CAPS));
693 		break;
694 	}
695 	return hfid->ops->bind(hfid, hbfid, flags);
696 }
697 
hook_debug_txrx_entry_init(struct ofi_bufpool_region * region,void * buf)698 static void hook_debug_txrx_entry_init(struct ofi_bufpool_region *region,
699 				       void *buf)
700 {
701 	struct hook_debug_txrx_entry *entry = buf;
702 	entry->magic = OFI_MAGIC_64;
703 	entry->ep = region->pool->attr.context;
704 }
705 
706 struct fi_ops hook_debug_ep_fid_ops;
707 static struct fi_ops_msg hook_debug_msg_ops = {
708 	.size 		= sizeof(struct fi_ops_msg),
709 	.recv 		= hook_debug_recv,
710 	.recvv 		= fi_no_msg_recvv,
711 	.recvmsg 	= hook_debug_recvmsg,
712 	.send		= hook_debug_send,
713 	.senddata 	= hook_debug_senddata,
714 	.sendv		= hook_debug_sendv,
715 	.sendmsg 	= hook_debug_sendmsg,
716 	.inject 	= hook_debug_inject,
717 	.injectdata 	= hook_debug_injectdata,
718 };
719 
720 struct fi_ops_tagged hook_debug_tagged_ops = {
721 	.recv 		= hook_debug_trecv,
722 	.recvv 		= fi_no_tagged_recvv,
723 	.recvmsg 	= fi_no_tagged_recvmsg,
724 	.send 		= hook_debug_tsend,
725 	.senddata 	= hook_debug_tsenddata,
726 	.sendv		= hook_debug_tsendv,
727 	.sendmsg 	= hook_debug_tsendmsg,
728 	.inject 	= hook_debug_tinject,
729 	.injectdata	= hook_debug_tinjectdata,
730 };
731 
hook_debug_endpoint(struct fid_domain * domain,struct fi_info * info,struct fid_ep ** ep,void * context)732 int hook_debug_endpoint(struct fid_domain *domain, struct fi_info *info,
733 			struct fid_ep **ep, void *context)
734 {
735 	struct hook_debug_ep *myep;
736 	struct ofi_bufpool_attr bufpool_attr = {
737 		.size		= sizeof(struct hook_debug_txrx_entry),
738 		.alignment	= 16,
739 		.max_cnt	= 0,
740 		.init_fn	= hook_debug_txrx_entry_init,
741 	};
742 
743 	int ret = -FI_ENOMEM;
744 
745 	if (info->domain_attr->threading != FI_THREAD_DOMAIN) {
746 		FI_WARN(&hook_debug_prov_ctx.prov, FI_LOG_CQ,
747 			"debug hooking provider doesn't support thread safety"
748 			"at this time\n");
749 		return -FI_EINVAL;
750 	}
751 
752 	FI_TRACE(hook_to_hprov(&domain->fid), FI_LOG_EP_CTRL,
753 		 "tx_attr->size: %zu\n", info->tx_attr->size);
754 	FI_TRACE(hook_to_hprov(&domain->fid), FI_LOG_EP_CTRL,
755 		 "rx_attr->size: %zu\n", info->rx_attr->size);
756 
757 	myep = calloc(1, sizeof *myep);
758 	if (!myep)
759 		return ret;
760 
761 	bufpool_attr.context = myep;
762 
763 	if (config.track_sends) {
764 		bufpool_attr.chunk_cnt = info->tx_attr->size;
765 		ret = ofi_bufpool_create_attr(&bufpool_attr, &myep->tx_pool);
766 		if (ret)
767 			goto err;
768 	}
769 
770 	if (config.track_recvs) {
771 		bufpool_attr.chunk_cnt = info->rx_attr->size;
772 		ret = ofi_bufpool_create_attr(&bufpool_attr, &myep->rx_pool);
773 		if (ret)
774 			goto err;
775 	}
776 
777 	ret = hook_endpoint_init(domain, info, ep, context, &myep->hook_ep);
778 	if (ret)
779 		goto err;
780 
781 	FI_TRACE(hook_to_hprov(&myep->hook_ep.ep.fid), FI_LOG_EP_CTRL,
782 		 "endpoint opened, fid: %p\n", &myep->hook_ep.hep->fid);
783 
784 	myep->hook_ep.ep.fid.ops = &hook_debug_ep_fid_ops;
785 	myep->hook_ep.ep.msg = &hook_debug_msg_ops;
786 	myep->hook_ep.ep.tagged = &hook_debug_tagged_ops;
787 	myep->tx_op_flags = info->tx_attr->op_flags;
788 	myep->rx_op_flags = info->rx_attr->op_flags;
789 
790 	return 0;
791 err:
792 	hook_debug_ep_close(&myep->hook_ep.ep.fid);
793 	return ret;
794 }
795 
796 /*
797  * EQ
798  */
799 
hook_debug_eq_read(struct fid_eq * eq,uint32_t * event,void * buf,size_t len,uint64_t flags)800 static ssize_t hook_debug_eq_read(struct fid_eq *eq, uint32_t *event,
801 				  void *buf, size_t len, uint64_t flags)
802 {
803 	struct hook_debug_eq *myeq = container_of(eq, struct hook_debug_eq,
804 						  hook_eq.eq);
805 	ssize_t ret;
806 
807 	ret = hook_eq_read(eq, event, buf, len, flags);
808 	if (ret > 0)
809 		ofi_atomic_inc64(&myeq->event_cntr[*event]);
810 
811 	hook_debug_trace_exit_eq(myeq, "fi_eq_read", (ssize_t)ret);
812 	return ret;
813 }
814 
hook_debug_eq_sread(struct fid_eq * eq,uint32_t * event,void * buf,size_t len,int timeout,uint64_t flags)815 static ssize_t hook_debug_eq_sread(struct fid_eq *eq, uint32_t *event,
816 				   void *buf, size_t len, int timeout,
817 				   uint64_t flags)
818 {
819 	struct hook_debug_eq *myeq = container_of(eq, struct hook_debug_eq,
820 						  hook_eq.eq);
821 	ssize_t ret;
822 
823 	ret = hook_eq_sread(eq, event, buf, len, timeout, flags);
824 	if (ret > 0)
825 		ofi_atomic_inc64(&myeq->event_cntr[*event]);
826 
827 	hook_debug_trace_exit_eq(myeq, "fi_eq_sread", (ssize_t)ret);
828 	return ret;
829 }
830 
hook_debug_eq_close(struct fid * fid)831 static int hook_debug_eq_close(struct fid *fid)
832 {
833 	struct hook_debug_eq *myeq = container_of(fid, struct hook_debug_eq,
834 						  hook_eq.eq.fid);
835 	int i, ret;
836 
837 	HOOK_DEBUG_TRACE(myeq->hook_eq.fabric, FI_LOG_EQ, "EQ events:\n");
838 
839 	for (i = 0; i < HOOK_DEBUG_EQ_EVENT_MAX; i++)
840 		HOOK_DEBUG_TRACE(myeq->hook_eq.fabric, FI_LOG_EQ,
841 				 "%-20s: %" PRIu64 "\n",
842 				 fi_tostr(&i, FI_TYPE_EQ_EVENT),
843 				 ofi_atomic_get64(&myeq->event_cntr[i]));
844 
845 	ret = fi_close(&myeq->hook_eq.heq->fid);
846 	if (!ret)
847 		free(myeq);
848 
849 	return ret;
850 }
851 
852 static struct fi_ops_eq hook_debug_eq_ops;
853 static struct fi_ops hook_debug_eq_fid_ops;
854 
hook_debug_eq_open(struct fid_fabric * fabric,struct fi_eq_attr * attr,struct fid_eq ** eq,void * context)855 int hook_debug_eq_open(struct fid_fabric *fabric, struct fi_eq_attr *attr,
856 		 struct fid_eq **eq, void *context)
857 {
858 	struct hook_debug_eq *myeq;
859 	int i, ret;
860 
861 	myeq = calloc(1, sizeof *myeq);
862 	if (!myeq)
863 		return -FI_ENOMEM;
864 
865 	ret = hook_eq_init(fabric, attr, eq, context, &myeq->hook_eq);
866 	if (ret)
867 		free(myeq);
868 
869 	myeq->hook_eq.eq.ops = &hook_debug_eq_ops;
870 	myeq->hook_eq.eq.fid.ops = &hook_debug_eq_fid_ops;
871 
872 	for (i = 0; i < HOOK_DEBUG_EQ_EVENT_MAX; i++)
873 		ofi_atomic_initialize64(&myeq->event_cntr[i], 0);
874 
875 	return 0;
876 }
877 
878 /*
879  * Fabric
880  */
881 
882 struct fi_ops hook_debug_fabric_fid_ops;
883 static struct fi_ops_fabric hook_debug_fabric_ops;
884 
hook_debug_fabric(struct fi_fabric_attr * attr,struct fid_fabric ** fabric,void * context)885 static int hook_debug_fabric(struct fi_fabric_attr *attr,
886 			    struct fid_fabric **fabric, void *context)
887 {
888 	struct fi_provider *hprov = context;
889 	struct hook_fabric *fab;
890 
891 	FI_TRACE(hprov, FI_LOG_FABRIC, "Installing debug hook\n");
892 	fab = calloc(1, sizeof *fab);
893 	if (!fab)
894 		return -FI_ENOMEM;
895 
896 	hook_fabric_init(fab, HOOK_DEBUG, attr->fabric, hprov,
897 			 &hook_debug_fabric_fid_ops, &hook_debug_prov_ctx);
898 	*fabric = &fab->fabric;
899 	fab->fabric.ops = &hook_debug_fabric_ops;
900 	return 0;
901 }
902 
903 struct hook_prov_ctx hook_debug_prov_ctx = {
904 	.prov = {
905 		.version = OFI_VERSION_DEF_PROV,
906 		/* We're a pass-through provider, so the fi_version is always the latest */
907 		.fi_version = OFI_VERSION_LATEST,
908 		.name = "ofi_hook_debug",
909 		.getinfo = NULL,
910 		.fabric = hook_debug_fabric,
911 		.cleanup = NULL,
912 	},
913 };
914 
hook_debug_cntr_read(struct fid_cntr * cntr)915 static uint64_t hook_debug_cntr_read(struct fid_cntr *cntr)
916 {
917 	struct hook_cntr *mycntr = container_of(cntr, struct hook_cntr, cntr);
918 	uint64_t ret;
919 
920 	ret = fi_cntr_read(mycntr->hcntr);
921 	hook_debug_trace_exit_cntr(mycntr, "fi_cntr_read", (ssize_t)ret);
922 	return ret;
923 }
924 
925 
hook_debug_cntr_wait(struct fid_cntr * cntr,uint64_t threshold,int timeout)926 static int hook_debug_cntr_wait(struct fid_cntr *cntr, uint64_t threshold, int timeout)
927 {
928 	struct hook_cntr *mycntr = container_of(cntr, struct hook_cntr, cntr);
929 	int ret;
930 
931 	HOOK_DEBUG_TRACE(mycntr->domain->fabric, FI_LOG_CNTR,
932 			 "cntr: %p, threshold: %" PRIu64 ", timeout: %d\n",
933 			 mycntr->hcntr, threshold, timeout);
934 
935 	ret = fi_cntr_wait(mycntr->hcntr, threshold, timeout);
936 
937 	hook_debug_trace_exit_cntr(mycntr, "fi_cntr_wait", (ssize_t)ret);
938 	return ret;
939 }
940 
941 static struct fi_ops_cntr hook_debug_cntr_ops;
942 
hook_debug_cntr_init(struct fid * fid)943 int hook_debug_cntr_init(struct fid *fid)
944 {
945 	struct hook_cntr *mycntr = container_of(fid, struct hook_cntr, cntr.fid);
946 	HOOK_DEBUG_TRACE(mycntr->domain->fabric, FI_LOG_CNTR,
947 			 "fi_cntr_open: %p\n", mycntr->hcntr);
948 	mycntr->cntr.ops = &hook_debug_cntr_ops;
949 	return 0;
950 }
951 
952 static struct fi_ops_domain hook_debug_domain_ops;
953 
hook_debug_domain_init(struct fid * fid)954 int hook_debug_domain_init(struct fid *fid)
955 {
956 	struct fid_domain *domain = container_of(fid, struct fid_domain, fid);
957 	domain->ops = &hook_debug_domain_ops;
958 	return 0;
959 }
960 
961 HOOK_DEBUG_INI
962 {
963 	// TODO explore adding a common hook_ini function that can initialize
964 	// the ops to common ones. Then override here.
965 	hook_debug_fabric_fid_ops = hook_fid_ops;
966 	hook_debug_fabric_ops = hook_fabric_ops;
967 	hook_debug_fabric_ops.eq_open = hook_debug_eq_open;
968 
969 	hook_debug_eq_fid_ops = hook_fid_ops;
970 	hook_debug_eq_fid_ops.close = hook_debug_eq_close;
971 	hook_debug_eq_ops = hook_eq_ops;
972 	hook_debug_eq_ops.read = hook_debug_eq_read;
973 	hook_debug_eq_ops.sread = hook_debug_eq_sread;
974 
975 	hook_debug_domain_ops = hook_domain_ops;
976 	hook_debug_domain_ops.cq_open = hook_debug_cq_open;
977 	hook_debug_domain_ops.endpoint = hook_debug_endpoint;
978 
979 	hook_debug_cq_fid_ops = hook_fid_ops;
980 	hook_debug_cq_fid_ops.close = hook_debug_cq_close;
981 
982 	hook_debug_cq_ops = hook_cq_ops;
983 	hook_debug_cq_ops.read = hook_debug_cq_read;
984 	hook_debug_cq_ops.readfrom = hook_debug_cq_readfrom;
985 	hook_debug_cq_ops.sread = fi_no_cq_sread;
986 	hook_debug_cq_ops.sreadfrom = fi_no_cq_sreadfrom;
987 
988 	hook_debug_cntr_ops = hook_cntr_ops;
989 	hook_debug_cntr_ops.read = hook_debug_cntr_read;
990 	hook_debug_cntr_ops.add = fi_no_cntr_add;
991 	hook_debug_cntr_ops.set = fi_no_cntr_set;
992 	hook_debug_cntr_ops.wait = hook_debug_cntr_wait;
993 
994 	hook_debug_ep_fid_ops = hook_fid_ops;
995 	hook_debug_ep_fid_ops.bind = hook_debug_ep_bind;
996 	hook_debug_ep_fid_ops.close = hook_debug_ep_close;
997 
998 	hook_debug_prov_ctx.ini_fid[FI_CLASS_DOMAIN] = hook_debug_domain_init;
999 	hook_debug_prov_ctx.ini_fid[FI_CLASS_CNTR] = hook_debug_cntr_init;
1000 	return &hook_debug_prov_ctx.prov;
1001 }
1002