1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
2 /*
3 * Copyright (c) 2004-2007 The Trustees of Indiana University and Indiana
4 * University Research and Technology
5 * Corporation. All rights reserved.
6 * Copyright (c) 2004-2011 The University of Tennessee and The University
7 * of Tennessee Research Foundation. All rights
8 * reserved.
9 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
10 * University of Stuttgart. All rights reserved.
11 * Copyright (c) 2004-2005 The Regents of the University of California.
12 * All rights reserved.
13 * Copyright (c) 2008-2009 Cisco Systems, Inc. All rights reserved.
14 * Copyright (c) 2015 Los Alamos National Security, LLC. All rights
15 * reserved.
16 * Copyright (c) 2018 Amazon.com, Inc. or its affiliates. All Rights reserved.
17 * $COPYRIGHT$
18 *
19 * Additional copyrights may follow
20 *
21 * $HEADER$
22 */
23
24 /** @file
25 *
26 * This is the "example" component source code. It contains the
27 * well-known struct that OMPI will dlsym() (or equivalent) for to
28 * find how to access the rest of the component and any modules that
29 * are created.
30 */
31
32 #include "ompi_config.h"
33
34 #include "opal/util/printf.h"
35
36 #include "ompi/constants.h"
37 #include "ompi/op/op.h"
38 #include "ompi/mca/op/op.h"
39 #include "ompi/mca/op/base/base.h"
40 #include "ompi/mca/op/example/op_example.h"
41
42 static int example_component_open(void);
43 static int example_component_close(void);
44 static int example_component_init_query(bool enable_progress_threads,
45 bool enable_mpi_thread_multiple);
46 static struct ompi_op_base_module_1_0_0_t *
47 example_component_op_query(struct ompi_op_t *op, int *priority);
48 static int example_component_register(void);
49
50 ompi_op_example_component_t mca_op_example_component = {
51 /* First, the mca_base_component_t struct containing meta
52 information about the component itself */
53 {
54 .opc_version = {
55 OMPI_OP_BASE_VERSION_1_0_0,
56
57 .mca_component_name = "example",
58 MCA_BASE_MAKE_VERSION(component, OMPI_MAJOR_VERSION, OMPI_MINOR_VERSION,
59 OMPI_RELEASE_VERSION),
60 .mca_open_component = example_component_open,
61 .mca_close_component = example_component_close,
62 .mca_register_component_params = example_component_register,
63 },
64 .opc_data = {
65 /* The component is checkpoint ready */
66 MCA_BASE_METADATA_PARAM_CHECKPOINT
67 },
68
69 .opc_init_query = example_component_init_query,
70 .opc_op_query = example_component_op_query,
71 },
72
73 /* Now comes the example-component-specific data. In this case,
74 we'll just leave it blank, defaulting all the values to
75 0/false/whatever. We'll fill them in with meaningful values
76 during _component_init_query(). */
77 };
78
79 /*
80 * Component open
81 */
82 static int example_component_open(void)
83 {
84 opal_output(ompi_op_base_framework.framework_output, "example component open");
85
86 /* A first level check to see if example is even available in this
87 process. E.g., you may want to do a first-order check to see
88 if hardware is available. If so, return OMPI_SUCCESS. If not,
89 return anything other than OMPI_SUCCESS and the component will
90 silently be ignored.
91
92 Note that if this function returns non-OMPI_SUCCESS, then this
93 component won't even be shown in ompi_info output (which is
94 probably not what you want).
95 */
96
97 return OMPI_SUCCESS;
98 }
99
100
101 /*
102 * Component close
103 */
104 static int example_component_close(void)
105 {
106 opal_output(ompi_op_base_framework.framework_output, "example component close");
107
108 /* If example was opened successfully, close it (i.e., release any
109 resources that may have been allocated on this component).
110 Note that _component_close() will always be called at the end
111 of the process, so it may have been after any/all of the other
112 component functions have been invoked (and possibly even after
113 modules have been created and/or destroyed). */
114
115 return OMPI_SUCCESS;
116 }
117
118 static char *example_component_version;
119
120 /*
121 * Register MCA params.
122 */
123 static int example_component_register(void)
124 {
125 int val;
126 char *str;
127
128 opal_output(ompi_op_base_framework.framework_output, "example component register");
129
130 /* Register any relevant MCA params. At a minimum, perhaps some
131 information MCA params that return version and capability
132 information. */
133
134 /* For example, let's make a string MCA information parameter
135 containing the major.minor.release version number from the
136 libfoo support library (see configure.m4 for how we got these C
137 macros). */
138 opal_asprintf(&str, "%s.%s.%s",
139 OP_EXAMPLE_LIBFOO_VERSION_MAJOR,
140 OP_EXAMPLE_LIBFOO_VERSION_MINOR,
141 OP_EXAMPLE_LIBFOO_VERSION_RELEASE);
142 example_component_version = str;
143 (void) mca_base_component_var_register(&mca_op_example_component.super.opc_version,
144 "libfoo_version",
145 "Version of the libfoo support library that this component was built against.",
146 MCA_BASE_VAR_TYPE_STRING, NULL, 0,
147 MCA_BASE_VAR_FLAG_DEFAULT_ONLY,
148 OPAL_INFO_LVL_9,
149 MCA_BASE_VAR_SCOPE_CONSTANT,
150 &example_component_version);
151 /* The variable system duplicated the string. */
152 free(str);
153
154 /* Additionally, since this component is simulating hardware,
155 let's make MCA params that determine whethere a) the hardware
156 is available, and b) whether double precision floating point
157 types are supported. This allows you to change the behavior of
158 this component at run-time (by setting these MCA params at
159 run-time), simulating different kinds of hardware. */
160 mca_op_example_component.hardware_available = false;
161 (void) mca_base_component_var_register(&mca_op_example_component.super.opc_version,
162 "hardware_available",
163 "Whether the hardware is available or not",
164 MCA_BASE_VAR_TYPE_BOOL, NULL, 0, 0,
165 OPAL_INFO_LVL_9,
166 MCA_BASE_VAR_SCOPE_READONLY,
167 &mca_op_example_component.hardware_available);
168
169 mca_op_example_component.double_supported = true;
170 (void) mca_base_component_var_register(&mca_op_example_component.super.opc_version,
171 "double_supported",
172 "Whether the double precision data types are supported or not",
173 MCA_BASE_VAR_TYPE_BOOL, NULL, 0, 0,
174 OPAL_INFO_LVL_9,
175 MCA_BASE_VAR_SCOPE_READONLY,
176 &mca_op_example_component.double_supported);
177
178 return OMPI_SUCCESS;
179 }
180
181
182 /*
183 * Query whether this component wants to be used in this process.
184 */
185 static int example_component_init_query(bool enable_progress_threads,
186 bool enable_mpi_thread_multiple)
187 {
188 opal_output(ompi_op_base_framework.framework_output, "example component init query");
189
190 /* Query to see if we have the desired hardware / resources to be
191 able to perform reduction operations. This is a much more
192 comprehensive check than _component_open().
193
194 If this component can be used in this process, return
195 OMPI_SUCCESS, meaning that we'll be queried later via during
196 the MPI_Op component selection process via
197 _component_op_query(). Otherwise, return anything other than
198 OMPI_SUCCESS and this component will be silently ignored for
199 the MPI_Op component selection process.
200
201 The input parameters enable_progress_threads and
202 enable_mpi_thread_multiple also tell the component the following:
203
204 - If enable_progress_threads==true, then the component is
205 allowed to have a progress thread in the background that is
206 supported by the OMPI infrastructure (i.e., all of OMPI's
207 locks and whatnot are active in this build). Note that the
208 component can *always* have a progress thread in the
209 background regardless of the value of this parameter as lone
210 as the HAVE_THREADS macro is true and the component uses its
211 own locking schemes (i.e., does not rely on external
212 OPAL/OMPI data structures to be thread safe). This flag
213 simply indicates whether OPAL/OMPI data structures are
214 multi-threaded safe and whether multi-threading sync/IPC
215 mechanisms in the OMPI code base are active.
216
217 - If enable_mpi_thread_multiple==true, then MPI_THREAD_MULTIPLE is
218 active.
219
220 Note that a component can uses these values to deactivate
221 themselves if multi-threading is not supported (keep in mind
222 that in MPI_THREAD_MULTIPLE scenarios, the same MPI_Op can be
223 used in multiple, concurrent operations in different threads).
224 Let's assume that this component does not support
225 MPI_THREAD_MULTIPLE, and will therefore deactivate itself if
226 MPI_THREAD_MULTIPLE is used.
227 */
228
229 /* Note that we used MCA parameters to fill in the
230 _component.hardware_available and _component.double_supported
231 values. Typically, you'd probe the hardware here and fill in
232 those values instead of using MCA parameters (the MCA params
233 are only used in this example to allow simulating different
234 types of hardware). */
235
236 /* If we have the hardware and are not using MPI_THREAD_MULITPLE,
237 return OMPI_SUCCESS (indicating that _component_op_query() will
238 be called in the future for each intrinsic MPI_Op). Otherwise,
239 return OMPI_ERR_NOT_SUPPORTED (indicating that this component
240 will be closed and discarded). */
241 if (mca_op_example_component.hardware_available && !enable_mpi_thread_multiple) {
242 return OMPI_SUCCESS;
243 }
244 return OMPI_ERR_NOT_SUPPORTED;
245 }
246
247
248 /*
249 * Query whether this component can be used for a specific op
250 */
251 static struct ompi_op_base_module_1_0_0_t *
252 example_component_op_query(struct ompi_op_t *op, int *priority)
253 {
254 ompi_op_base_module_t *module = NULL;
255
256 opal_output(ompi_op_base_framework.framework_output, "example component op query");
257
258 /* Sanity check -- although the framework should never invoke the
259 _component_op_query() on non-intrinsic MPI_Op's, we'll put a
260 check here just to be sure. */
261 if (0 == (OMPI_OP_FLAGS_INTRINSIC & op->o_flags)) {
262 opal_output(0, "example component op query: not an intrinsic MPI_Op -- skipping");
263 return NULL;
264 }
265
266 /* What follows is an example of how to determine whether your
267 component supports the queried MPI_Op. You can do this lots of
268 different ways; this is but one example. */
269
270 /* Note that we *do* have the hardware; _component_init_query()
271 would not have returned OMPI_SUCCESS if we didn't have the
272 hardware (and therefore this function would never have been
273 called). So we don't need to check for the hardware again.
274 Instead, we need to do finer-grained checks (e.g., do we
275 support this op, and if so, what datatypes are supported?).
276
277 So check to see whether this MPI_Op operation is supported on
278 the hardware that this component supports (which may involve
279 querying the hardware to see what it is capable of).
280
281 You can see what operation is being requested by checking the
282 "op->o_f_to_c_index" value against the OMPI_OP_BASE_FORTRAN_*
283 enums. See ompi/mca/op/op.h for a full list of the
284 OMPI_OP_BASE_FORTRAN_* enums.
285
286 In this example component, we support MAX and BXOR. */
287 switch (op->o_f_to_c_index) {
288 case OMPI_OP_BASE_FORTRAN_MAX:
289 /* Corresponds to MPI_MAX */
290 module = ompi_op_example_setup_max(op);
291 break;
292
293 case OMPI_OP_BASE_FORTRAN_BXOR:
294 /* Corresponds to MPI_BXOR */
295 module = ompi_op_example_setup_bxor(op);
296 break;
297 }
298
299 /* If we got a module from above, we'll return it. Otherwise,
300 we'll return NULL, indicating that this component does not want
301 to be considered for selection for this MPI_Op. Note that the
302 "setup" functions each returned a *example* component pointer
303 (vs. a *base* component pointer -- where an *example* component
304 is a base component plus some other module-specific cached
305 information), so we have to cast it to the right pointer type
306 before returning. */
307 if (NULL != module) {
308 *priority = 50;
309 }
310 return (ompi_op_base_module_1_0_0_t *) module;
311 }