1 /* -*- Mode: C; c-basic-offset:4 ; -*- */
2 /*
3 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
4 * University Research and Technology
5 * Corporation. All rights reserved.
6 * Copyright (c) 2004-2009 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) 2007 Lawrence Livermore National Security, LLC. All
14 * rights reserved.
15 * Copyright (c) 2008 Sun Microsystems, Inc. All rights reserved.
16 * Copyright (c) 2008-2015 Cisco Systems, Inc. All rights reserved.
17 * Copyright (c) 2020 Research Organization for Information Science
18 * and Technology (RIST). All rights reserved.
19 * $COPYRIGHT$
20 *
21 * Additional copyrights may follow
22 *
23 * $HEADER$
24 */
25
26 #include "ompi_config.h"
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include "mpi.h"
33 #include "ompi/constants.h"
34
35 #include "opal/util/output.h"
36 #include "opal/class/opal_list.h"
37 #include "opal/class/opal_object.h"
38 #include "ompi/mca/mca.h"
39 #include "opal/mca/base/base.h"
40
41
42 #include "ompi/op/op.h"
43 #include "ompi/mca/op/op.h"
44 #include "ompi/mca/op/base/base.h"
45 #include "ompi/mca/op/base/functions.h"
46
47
48 /*
49 * Local types
50 */
51 typedef struct avail_op_t {
52 opal_list_item_t super;
53
54 int ao_priority;
55 ompi_op_base_module_1_0_0_t *ao_module;
56 } avail_op_t;
57
58
59 /*
60 * Local functions
61 */
62 static opal_list_t *check_components(opal_list_t *components,
63 ompi_op_t *op);
64 static int check_one_component(ompi_op_t *op,
65 const mca_base_component_t *component,
66 ompi_op_base_module_1_0_0_t **module);
67
68 static int query(const mca_base_component_t *component,
69 ompi_op_t *op, int *priority,
70 ompi_op_base_module_1_0_0_t **module);
71
72 static int query_1_0_0(const ompi_op_base_component_1_0_0_t *op_component,
73 ompi_op_t *op, int *priority,
74 ompi_op_base_module_1_0_0_t **module);
75
76 /*
77 * Stuff for the OBJ interface
78 */
79 static OBJ_CLASS_INSTANCE(avail_op_t, opal_list_item_t, NULL, NULL);
80
81
82 /*
83 * This function is called at the initialization time of every
84 * *intrinsic* MPI_Op (it is *not* used for user-defined MPI_Ops!).
85 * It is used to select which op component(s) will be active for a
86 * given MPI_Op.
87 *
88 * This selection logic is not for the weak.
89 */
ompi_op_base_op_select(ompi_op_t * op)90 int ompi_op_base_op_select(ompi_op_t *op)
91 {
92 int i, ret;
93 opal_list_t *selectable;
94 opal_list_item_t *item;
95 ompi_op_base_module_t *module;
96
97 /* Announce */
98 opal_output_verbose(10, ompi_op_base_framework.framework_output,
99 "op:base:op_select: new op: %s",
100 op->o_name);
101
102 /* Make a module for all the base functions so that other modules
103 can RETAIN it (vs. having NULL for the base function modules,
104 and forcing all other modules to check for NULL before calling
105 RETAIN). */
106 module = OBJ_NEW(ompi_op_base_module_t);
107
108 /* Initialize all functions to point to the corresponding base
109 functions. Set the corresponding module pointers to NULL,
110 indicating that these are base functions with no corresponding
111 module. */
112 memset(&op->o_func, 0, sizeof(op->o_func));
113 memset(&op->o_3buff_intrinsic, 0, sizeof(op->o_3buff_intrinsic));
114 for (i = 0; i < OMPI_OP_BASE_TYPE_MAX; ++i) {
115 op->o_func.intrinsic.fns[i] =
116 ompi_op_base_functions[op->o_f_to_c_index][i];
117 op->o_func.intrinsic.modules[i] = module;
118 OBJ_RETAIN(module);
119 op->o_3buff_intrinsic.fns[i] =
120 ompi_op_base_3buff_functions[op->o_f_to_c_index][i];
121 op->o_3buff_intrinsic.modules[i] = module;
122 OBJ_RETAIN(module);
123 }
124
125 /* Offset the initial OBJ_NEW */
126 OBJ_RELEASE(module);
127
128 /* Check for any components that want to run. It's not an error
129 if there are none; we'll just use all the base functions in
130 this case. */
131 opal_output_verbose(10, ompi_op_base_framework.framework_output,
132 "op:base:op_select: Checking all available components");
133 selectable = check_components(&ompi_op_base_framework.framework_components, op);
134
135 /* Do the selection loop. The selectable list is in priority
136 order; lowest priority first. */
137 for (item = opal_list_remove_first(selectable);
138 NULL != item;
139 item = opal_list_remove_first(selectable)) {
140 avail_op_t *avail = (avail_op_t*) item;
141
142 /* Enable the module */
143 if (NULL != avail->ao_module->opm_enable) {
144 ret = avail->ao_module->opm_enable(avail->ao_module, op);
145 if (OMPI_SUCCESS != ret) {
146 /* If the module fails to enable, just release it and move
147 on */
148 OBJ_RELEASE(avail->ao_module);
149 OBJ_RELEASE(avail);
150 continue;
151 }
152 }
153
154 /* Copy over the non-NULL pointers */
155 for (i = 0; i < OMPI_OP_BASE_TYPE_MAX; ++i) {
156 /* 2-buffer variants */
157 if (NULL != avail->ao_module->opm_fns[i]) {
158 OBJ_RELEASE(op->o_func.intrinsic.modules[i]);
159 op->o_func.intrinsic.fns[i] = avail->ao_module->opm_fns[i];
160 op->o_func.intrinsic.modules[i] = avail->ao_module;
161 OBJ_RETAIN(avail->ao_module);
162 }
163
164 /* 3-buffer variants */
165 if (NULL != avail->ao_module->opm_3buff_fns[i]) {
166 OBJ_RELEASE(op->o_func.intrinsic.modules[i]);
167 op->o_3buff_intrinsic.fns[i] =
168 avail->ao_module->opm_3buff_fns[i];
169 op->o_3buff_intrinsic.modules[i] = avail->ao_module;
170 OBJ_RETAIN(avail->ao_module);
171 }
172 }
173
174 /* release the original module reference and the list item */
175 OBJ_RELEASE(avail->ao_module);
176 OBJ_RELEASE(avail);
177 }
178
179 /* Done with the list from the check_components() call so release it. */
180 OBJ_RELEASE(selectable);
181
182 /* Sanity check: for intrinsic MPI_Ops, we should have exactly the
183 same pointers non-NULL as the corresponding initial table row
184 in ompi_op_base_functions / ompi_op_base_3buff_functions. The
185 values may be different, of course, but the pattern of
186 NULL/non-NULL should be exactly the same. */
187 for (i = 0; i < OMPI_OP_BASE_TYPE_MAX; ++i) {
188 if ((NULL == ompi_op_base_functions[op->o_f_to_c_index][i] &&
189 NULL != op->o_func.intrinsic.fns[i]) ||
190 (NULL != ompi_op_base_functions[op->o_f_to_c_index][i] &&
191 NULL == op->o_func.intrinsic.fns[i])) {
192 /* Oops -- we found a mismatch. This shouldn't happen; so
193 go release everything and return an error (yes, re-use
194 the "i" index because we're going to return without
195 completing the outter loop). */
196 for (i = 0; i < OMPI_OP_BASE_TYPE_MAX; ++i) {
197 OBJ_RELEASE(op->o_func.intrinsic.modules[i]);
198 op->o_func.intrinsic.modules[i] = NULL;
199 op->o_func.intrinsic.fns[i] = NULL;
200 }
201 return OMPI_ERR_NOT_FOUND;
202 }
203 }
204
205 return OMPI_SUCCESS;
206 }
207
avail_op_compare(opal_list_item_t ** itema,opal_list_item_t ** itemb)208 static int avail_op_compare(opal_list_item_t **itema,
209 opal_list_item_t **itemb)
210 {
211 avail_op_t *availa = (avail_op_t *) *itema;
212 avail_op_t *availb = (avail_op_t *) *itemb;
213
214 if (availa->ao_priority > availb->ao_priority) {
215 return 1;
216 } else if (availa->ao_priority < availb->ao_priority) {
217 return -1;
218 } else {
219 return 0;
220 }
221 }
222
223 /*
224 * For each module in the list, check and see if it wants to run, and
225 * do the resulting priority comparison. Make a list of modules to be
226 * only those who returned that they want to run, and put them in
227 * priority order (lowest to highest).
228 */
check_components(opal_list_t * components,ompi_op_t * op)229 static opal_list_t *check_components(opal_list_t *components,
230 ompi_op_t *op)
231 {
232 int priority;
233 mca_base_component_list_item_t *cli;
234 const mca_base_component_t *component;
235 ompi_op_base_module_1_0_0_t *module;
236 opal_list_t *selectable;
237 avail_op_t *avail;
238
239 /* Make a list of the components that query successfully */
240 selectable = OBJ_NEW(opal_list_t);
241
242 /* Scan through the list of components. This nested loop is O(N^2),
243 but we should never have too many components and/or names, so this
244 *hopefully* shouldn't matter... */
245
246 OPAL_LIST_FOREACH(cli, components, mca_base_component_list_item_t) {
247 component = cli->cli_component;
248
249 priority = check_one_component(op, component, &module);
250 if (priority >= 0) {
251 /* We have a component that indicated that it wants to run by
252 giving us a module */
253 avail = OBJ_NEW(avail_op_t);
254 avail->ao_priority = priority;
255 avail->ao_module = module;
256
257 opal_list_append(selectable, (opal_list_item_t*)avail);
258 }
259 }
260
261 opal_list_sort(selectable, avail_op_compare);
262
263 /* All done (even if the list is empty; that's ok) */
264 return selectable;
265 }
266
267
268 /*
269 * Check a single component
270 */
check_one_component(ompi_op_t * op,const mca_base_component_t * component,ompi_op_base_module_1_0_0_t ** module)271 static int check_one_component(ompi_op_t *op,
272 const mca_base_component_t *component,
273 ompi_op_base_module_1_0_0_t **module)
274 {
275 int err;
276 int priority = -1;
277
278 err = query(component, op, &priority, module);
279
280 if (OMPI_SUCCESS == err) {
281 priority = (priority < 100) ? priority : 100;
282 opal_output_verbose(10, ompi_op_base_framework.framework_output,
283 "op:base:op_select: component available: %s, priority: %d",
284 component->mca_component_name, priority);
285
286 } else {
287 priority = -1;
288 opal_output_verbose(10, ompi_op_base_framework.framework_output,
289 "op:base:op_select: component not available: %s",
290 component->mca_component_name);
291 }
292
293 return priority;
294 }
295
296
297 /**************************************************************************
298 * Query functions
299 **************************************************************************/
300
301 /*
302 * Take any version of a op module, query it, and return the right
303 * module struct
304 */
query(const mca_base_component_t * component,ompi_op_t * op,int * priority,ompi_op_base_module_1_0_0_t ** module)305 static int query(const mca_base_component_t *component,
306 ompi_op_t *op,
307 int *priority, ompi_op_base_module_1_0_0_t **module)
308 {
309 *module = NULL;
310 if (1 == component->mca_type_major_version &&
311 0 == component->mca_type_minor_version &&
312 0 == component->mca_type_release_version) {
313 const ompi_op_base_component_1_0_0_t *op100 =
314 (ompi_op_base_component_1_0_0_t *) component;
315
316 return query_1_0_0(op100, op, priority, module);
317 }
318
319 /* Unknown op API version -- return error */
320
321 return OMPI_ERROR;
322 }
323
324
query_1_0_0(const ompi_op_base_component_1_0_0_t * component,ompi_op_t * op,int * priority,ompi_op_base_module_1_0_0_t ** module)325 static int query_1_0_0(const ompi_op_base_component_1_0_0_t *component,
326 ompi_op_t *op, int *priority,
327 ompi_op_base_module_1_0_0_t **module)
328 {
329 ompi_op_base_module_1_0_0_t *ret;
330
331 /* There's currently no need for conversion */
332
333 ret = component->opc_op_query(op, priority);
334 if (NULL != ret) {
335 *module = ret;
336 return OMPI_SUCCESS;
337 }
338
339 return OMPI_ERROR;
340 }
341