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 }