/* * Copyright (c) 2013-2018 Mellanox Technologies, Inc. * All rights reserved. * Copyright (c) 2016 Cisco Systems, Inc. All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ */ #include "oshmem_config.h" #include #include #include #include "oshmem/constants.h" #include "opal/class/opal_list.h" #include "oshmem/mca/mca.h" #include "opal/mca/base/base.h" #include "opal/mca/base/mca_base_component_repository.h" #include "ompi/util/timings.h" #include "oshmem/util/oshmem_util.h" #include "oshmem/mca/scoll/scoll.h" #include "oshmem/mca/scoll/base/base.h" #include "oshmem/proc/proc.h" #include "oshmem/runtime/runtime.h" /* * Local types */ struct avail_com_t { opal_list_item_t super; int ac_priority; mca_scoll_base_module_t *ac_module; }; typedef struct avail_com_t avail_com_t; /* * Local functions */ static opal_list_t *check_components(opal_list_t * components, oshmem_group_t * group); static int check_one_component(oshmem_group_t * group, const mca_base_component_t * component, mca_scoll_base_module_1_0_0_t ** module); static int query(const mca_base_component_t * component, oshmem_group_t * group, int *priority, mca_scoll_base_module_1_0_0_t ** module); static int query_1_0_0(const mca_scoll_base_component_1_0_0_t * scoll_component, oshmem_group_t * group, int *priority, mca_scoll_base_module_1_0_0_t ** module); static int scoll_null_barrier(struct oshmem_group_t *group, long *pSync, int alg) { if (oshmem_proc_group_is_member(group)) { SCOLL_ERROR("internal error"); oshmem_shmem_abort(-1); return OSHMEM_ERROR; } return OSHMEM_SUCCESS; } static int scoll_null_broadcast(struct oshmem_group_t *group, int PE_root, void *target, const void *source, size_t nlong, long *pSync, bool nlong_type, int alg) { if (oshmem_proc_group_is_member(group)) { SCOLL_ERROR("internal error"); oshmem_shmem_abort(-1); return OSHMEM_ERROR; } return OSHMEM_SUCCESS; } static int scoll_null_collect(struct oshmem_group_t *group, void *target, const void *source, size_t nlong, long *pSync, bool nlong_type, int alg) { if (oshmem_proc_group_is_member(group)) { SCOLL_ERROR("internal error"); oshmem_shmem_abort(-1); return OSHMEM_ERROR; } return OSHMEM_SUCCESS; } static int scoll_null_reduce(struct oshmem_group_t *group, struct oshmem_op_t *op, void *target, const void *source, size_t nlong, long *pSync, void *pWrk, int alg) { if (oshmem_proc_group_is_member(group)) { SCOLL_ERROR("internal error"); oshmem_shmem_abort(-1); return OSHMEM_ERROR; } return OSHMEM_SUCCESS; } static int scoll_null_alltoall(struct oshmem_group_t *group, void *target, const void *source, ptrdiff_t dst, ptrdiff_t sst, size_t nlong, size_t element_size, long *pSync, int alg) { if (oshmem_proc_group_is_member(group)) { SCOLL_ERROR("internal error"); oshmem_shmem_abort(-1); return OSHMEM_ERROR; } return OSHMEM_SUCCESS; } /* * Stuff for the OBJ interface */ static OBJ_CLASS_INSTANCE(avail_com_t, opal_list_item_t, NULL, NULL); #define COPY(module, group, func) \ do { \ if (NULL != module->scoll_ ## func) { \ if (NULL != group->g_scoll.scoll_ ## func ## _module) { \ OBJ_RELEASE(group->g_scoll.scoll_ ## func ## _module); \ } \ group->g_scoll.scoll_ ## func = module->scoll_ ## func; \ group->g_scoll.scoll_ ## func ## _module = module; \ OBJ_RETAIN(module); \ } \ } while (0) #define CLOSE(group, func) \ do { \ if (NULL != group->g_scoll.scoll_ ## func ## _module) { \ OBJ_RELEASE(group->g_scoll.scoll_ ## func ## _module); \ group->g_scoll.scoll_## func = NULL; \ group->g_scoll.scoll_## func ## _module = NULL; \ } \ } while (0) int mca_scoll_base_group_unselect(struct oshmem_group_t * group) { /* * scoll close() is called before group destructors, so * do close group collectives if scoll modules are no longer * valid * * there is a memory leak here, because not doing close means * that we leaving object with dangling ref counts */ SCOLL_VERBOSE(10, "scoll:base:group_unselect: group: %d", group->id); CLOSE(group, barrier); CLOSE(group, broadcast); CLOSE(group, collect); CLOSE(group, reduce); CLOSE(group, alltoall); /* All done */ return OSHMEM_SUCCESS; } /* * This function is called at the initialization time of every * group. It is used to select which coll component will be * active for a given group. */ int mca_scoll_base_select(struct oshmem_group_t *group) { opal_list_t *selectable; opal_list_item_t *item; int ret; OPAL_TIMING_ENV_INIT(mca_scoll_base_select); /* Announce */ SCOLL_VERBOSE(10, "scoll:base:group_select: new group: %d", group->id); mca_scoll_base_group_unselect(group); memset(&group->g_scoll, 0, sizeof(mca_scoll_base_group_scoll_t)); if (!oshmem_proc_group_is_member(group)) { group->g_scoll.scoll_barrier = scoll_null_barrier; group->g_scoll.scoll_broadcast = scoll_null_broadcast; group->g_scoll.scoll_collect = scoll_null_collect; group->g_scoll.scoll_reduce = scoll_null_reduce; group->g_scoll.scoll_alltoall = scoll_null_alltoall; return OSHMEM_SUCCESS; } OPAL_TIMING_ENV_NEXT(mca_scoll_base_select, "setup"); SCOLL_VERBOSE(10, "scoll:base:group_select: Checking all available modules"); selectable = check_components(&oshmem_scoll_base_framework.framework_components, group); /* Upon return from the above, the modules list will contain the list of modules that returned (priority >= 0). If we have no collective modules available, then print error and return. */ if (NULL == selectable) { /* There's no modules available */ return OSHMEM_ERROR; } OPAL_TIMING_ENV_NEXT(mca_scoll_base_select, "check_components"); /* do the selection loop */ for (item = opal_list_remove_first(selectable); NULL != item; item = opal_list_remove_first(selectable)) { avail_com_t *avail = (avail_com_t *) item; ret = avail->ac_module->scoll_module_enable(avail->ac_module, group); if (OSHMEM_SUCCESS != ret) { mca_scoll_base_group_unselect(group); } else { COPY(avail->ac_module, group, barrier); COPY(avail->ac_module, group, broadcast); COPY(avail->ac_module, group, collect); COPY(avail->ac_module, group, reduce); COPY(avail->ac_module, group, alltoall); } OBJ_RELEASE(avail->ac_module); OBJ_RELEASE(avail); } OPAL_TIMING_ENV_NEXT(mca_scoll_base_select, "select_loop"); /* Done with the list from the check_components() call so release it. */ OBJ_RELEASE(selectable); if ((NULL == group->g_scoll.scoll_barrier) || (NULL == group->g_scoll.scoll_broadcast) || (NULL == group->g_scoll.scoll_collect) || (NULL == group->g_scoll.scoll_reduce) || (NULL == group->g_scoll.scoll_alltoall)) { mca_scoll_base_group_unselect(group); return OSHMEM_ERR_NOT_FOUND; } OPAL_TIMING_ENV_NEXT(mca_scoll_base_select, "release"); return OSHMEM_SUCCESS; } static int avail_coll_compare(opal_list_item_t **a, opal_list_item_t **b) { avail_com_t *acom = (avail_com_t *) *a; avail_com_t *bcom = (avail_com_t *) *b; if (acom->ac_priority > bcom->ac_priority) { return 1; } else if (acom->ac_priority < bcom->ac_priority) { return -1; } return 0; } /* * For each module in the list, check and see if it wants to run, and * do the resulting priority comparison. Make a list of modules to be * only those who returned that they want to run, and put them in * priority order. */ static opal_list_t *check_components(opal_list_t *components, oshmem_group_t *group) { int priority; const mca_base_component_t *component; mca_base_component_list_item_t *cli; mca_scoll_base_module_1_0_0_t *module; opal_list_t *selectable; avail_com_t *avail; /* Make a list of the components that query successfully */ selectable = OBJ_NEW(opal_list_t); /* Scan through the list of components */ OPAL_LIST_FOREACH(cli, &oshmem_scoll_base_framework.framework_components, mca_base_component_list_item_t) { component = cli->cli_component; priority = check_one_component(group, component, &module); if (priority >= 0) { /* We have a component that indicated that it wants to run by giving us a module */ avail = OBJ_NEW(avail_com_t); avail->ac_priority = priority; avail->ac_module = module; opal_list_append(selectable, &avail->super); } } /* If we didn't find any available components, return an error */ if (0 == opal_list_get_size(selectable)) { OBJ_RELEASE(selectable); return NULL; } /* Put this list in priority order */ opal_list_sort(selectable, avail_coll_compare); /* All done */ return selectable; } /* * Check a single component */ static int check_one_component(oshmem_group_t *group, const mca_base_component_t *component, mca_scoll_base_module_1_0_0_t **module) { int err; int priority = -1; err = query(component, group, &priority, module); if (OSHMEM_SUCCESS == err) { priority = (priority < 100) ? priority : 100; SCOLL_VERBOSE(10, "scoll:base:group_select: component available: %s, priority: %d", component->mca_component_name, priority); } else { priority = -1; SCOLL_VERBOSE(10, "scoll:base:group_select: component not available: %s", component->mca_component_name); } return priority; } /************************************************************************** * Query functions **************************************************************************/ /* * Take any version of a coll module, query it, and return the right * module struct */ static int query(const mca_base_component_t * component, oshmem_group_t *group, int *priority, mca_scoll_base_module_1_0_0_t **module) { *module = NULL; if (1 == component->mca_type_major_version && 0 == component->mca_type_minor_version && 0 == component->mca_type_release_version) { const mca_scoll_base_component_1_0_0_t *coll100 = (mca_scoll_base_component_1_0_0_t *) component; return query_1_0_0(coll100, group, priority, module); } /* Unknown coll API version -- return error */ return OSHMEM_ERROR; } static int query_1_0_0(const mca_scoll_base_component_1_0_0_t *component, oshmem_group_t *group, int *priority, mca_scoll_base_module_1_0_0_t **module) { mca_scoll_base_module_1_0_0_t *ret; /* There's currently no need for conversion */ ret = component->scoll_query(group, priority); if (NULL != ret) { *module = ret; return OSHMEM_SUCCESS; } return OSHMEM_ERROR; }