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