1 /**
2  * Copyright (C) Mellanox Technologies Ltd. 2001-2017.  ALL RIGHTS RESERVED.
3  *
4  * See file LICENSE for terms.
5  */
6 
7 
8 #ifndef UCM_UTIL_REPLACE_H_
9 #define UCM_UTIL_REPLACE_H_
10 
11 #include <ucm/bistro/bistro.h>
12 #include <ucs/datastruct/list.h>
13 #include <ucs/type/status.h>
14 #include <pthread.h>
15 
16 extern pthread_mutex_t ucm_reloc_get_orig_lock;
17 extern pthread_t volatile ucm_reloc_get_orig_thread;
18 
19 /**
20  * Define a replacement function to a memory-mapping function call, which calls
21  * the event handler, and if event handler returns error code - calls the original
22  * function.
23  */
24 
25 /* Due to CUDA API redifinition we have to create proxy macro to eliminate
26  * redifinition of internal finction names */
27 #define UCM_DEFINE_REPLACE_FUNC(_name, _rettype, _fail_val, ...) \
28     _UCM_DEFINE_REPLACE_FUNC(ucm_override_##_name, ucm_##_name, _rettype, _fail_val, __VA_ARGS__)
29 
30 #define _UCM_DEFINE_REPLACE_FUNC(_over_name, _ucm_name, _rettype, _fail_val, ...) \
31     \
32     /* Define a symbol which goes to the replacement - in case we are loaded first */ \
33     _rettype _over_name(UCM_FUNC_DEFINE_ARGS(__VA_ARGS__)) \
34     { \
35         _rettype res; \
36         UCM_BISTRO_PROLOGUE; \
37         ucm_trace("%s()", __FUNCTION__); \
38         \
39         if (ucs_unlikely(ucm_reloc_get_orig_thread == pthread_self())) { \
40             return (_rettype)_fail_val; \
41         } \
42         res = _ucm_name(UCM_FUNC_PASS_ARGS(__VA_ARGS__)); \
43         UCM_BISTRO_EPILOGUE; \
44         return res; \
45     }
46 
47 #define UCM_OVERRIDE_FUNC(_name, _rettype) \
48     _rettype _name() __attribute__ ((alias (UCS_PP_QUOTE(ucm_override_##_name)))); \
49 
50 #define UCM_DEFINE_DLSYM_FUNC(_name, _rettype, _fail_val, ...) \
51     _UCM_DEFINE_DLSYM_FUNC(_name, ucm_orig_##_name, ucm_override_##_name, \
52                           _rettype, _fail_val, __VA_ARGS__)
53 
54 #define _UCM_DEFINE_DLSYM_FUNC(_name, _orig_name, _over_name, _rettype, _fail_val, ...) \
55     _rettype _over_name(UCM_FUNC_DEFINE_ARGS(__VA_ARGS__)); \
56     \
57     /* Call the original function using dlsym(RTLD_NEXT) */ \
58     _rettype _orig_name(UCM_FUNC_DEFINE_ARGS(__VA_ARGS__)) \
59     { \
60         typedef _rettype (*func_ptr_t) (__VA_ARGS__); \
61         static func_ptr_t orig_func_ptr = NULL; \
62         \
63         ucm_trace("%s()", __FUNCTION__); \
64         \
65         if (ucs_unlikely(orig_func_ptr == NULL)) { \
66             pthread_mutex_lock(&ucm_reloc_get_orig_lock); \
67             ucm_reloc_get_orig_thread = pthread_self(); \
68             orig_func_ptr = (func_ptr_t)ucm_reloc_get_orig(UCS_PP_QUOTE(_name), \
69                                                            _over_name); \
70             ucm_reloc_get_orig_thread = (pthread_t)-1; \
71             pthread_mutex_unlock(&ucm_reloc_get_orig_lock); \
72         } \
73         return orig_func_ptr(UCM_FUNC_PASS_ARGS(__VA_ARGS__)); \
74     }
75 
76 #define UCM_DEFINE_REPLACE_DLSYM_FUNC(_name, _rettype, _fail_val, ...) \
77     _UCM_DEFINE_DLSYM_FUNC(_name, ucm_orig_##_name, ucm_override_##_name, \
78                           _rettype, _fail_val, __VA_ARGS__) \
79     _UCM_DEFINE_REPLACE_FUNC(ucm_override_##_name, ucm_##_name, \
80                              _rettype, _fail_val, __VA_ARGS__)
81 
82 #define UCM_DEFINE_SYSCALL_FUNC(_name, _rettype, _syscall_id, ...) \
83     /* Call syscall */ \
84     _rettype ucm_orig_##_name(UCM_FUNC_DEFINE_ARGS(__VA_ARGS__)) \
85     { \
86         return (_rettype)syscall(_syscall_id, UCM_FUNC_PASS_ARGS(__VA_ARGS__)); \
87     }
88 
89 #if UCM_BISTRO_HOOKS
90 #  define UCM_DEFINE_SELECT_FUNC(_name, _rettype, _fail_val, _syscall_id, ...) \
91     _UCM_DEFINE_DLSYM_FUNC(_name, ucm_orig_##_name##_dlsym, ucm_override_##_name, \
92                           _rettype, _fail_val, __VA_ARGS__) \
93     UCM_DEFINE_SYSCALL_FUNC(_name##_syscall, _rettype, _syscall_id, __VA_ARGS__) \
94     _rettype ucm_orig_##_name(UCM_FUNC_DEFINE_ARGS(__VA_ARGS__)) \
95     { \
96         return (ucm_mmap_hook_mode() == UCM_MMAP_HOOK_BISTRO) ? \
97                ucm_orig_##_name##_syscall(UCM_FUNC_PASS_ARGS(__VA_ARGS__)) : \
98                ucm_orig_##_name##_dlsym(UCM_FUNC_PASS_ARGS(__VA_ARGS__)); \
99     }
100 #else
101 #  define UCM_DEFINE_SELECT_FUNC(_name, _rettype, _fail_val, _syscall_id, ...) \
102     UCM_DEFINE_DLSYM_FUNC(_name, _rettype, _fail_val, __VA_ARGS__)
103 #endif
104 
105 /*
106  * Define argument list with given types.
107  */
108 #define UCM_FUNC_DEFINE_ARGS(...) \
109     UCS_PP_FOREACH_SEP(_UCM_FUNC_ARG_DEFINE, _, \
110                        UCS_PP_ZIP((UCS_PP_SEQ(UCS_PP_NUM_ARGS(__VA_ARGS__))), \
111                                   (__VA_ARGS__)))
112 
113 /*
114  * Pass auto-generated arguments to a function call.
115  */
116 #define UCM_FUNC_PASS_ARGS(...) \
117     UCS_PP_FOREACH_SEP(_UCM_FUNC_ARG_PASS, _, UCS_PP_SEQ(UCS_PP_NUM_ARGS(__VA_ARGS__)))
118 
119 
120 /*
121  * Helpers
122  */
123 #define _UCM_FUNC_ARG_DEFINE(_, _bundle) \
124     __UCM_FUNC_ARG_DEFINE(_, UCS_PP_TUPLE_0 _bundle, UCS_PP_TUPLE_1 _bundle)
125 #define __UCM_FUNC_ARG_DEFINE(_, _index, _type) \
126     _type UCS_PP_TOKENPASTE(arg, _index)
127 #define _UCM_FUNC_ARG_PASS(_, _index) \
128     UCS_PP_TOKENPASTE(arg, _index)
129 
130 #endif
131