/* * Copyright (c) 2011 Oak Ridge National Labs. All rights reserved. * Copyright (c) 2013 Cisco Systems, Inc. All rights reserved. * $COPYRIGHT$ * * Additional copyrights may follow * * $HEADER$ */ /** * Processing for command line interface options * */ #include "rmaps_lama.h" #include "opal/util/argv.h" #include "orte/mca/rmaps/base/rmaps_private.h" #include "orte/mca/rmaps/base/base.h" #include "orte/util/show_help.h" #include /********************************* * Local Functions *********************************/ /* * QSort: Integer comparison */ static int lama_parse_int_sort(const void *a, const void *b); /* * Convert the '-ppr' syntax from the 'ppr' component to the 'lama' '-mppr' syntax. */ static char * rmaps_lama_covert_ppr(char * given_ppr); /********************************* * Parsing Functions *********************************/ int rmaps_lama_process_alias_params(orte_job_t *jdata) { int exit_status = ORTE_SUCCESS; /* * Mapping options * Note: L1, L2, L3 are not exposed in orterun to the user, so * there is no need to specify them here. */ if( NULL == rmaps_lama_cmd_map ) { /* orte_rmaps_base.mapping */ switch( ORTE_GET_MAPPING_POLICY(jdata->map->mapping) ) { case ORTE_MAPPING_BYNODE: /* rmaps_lama_cmd_map = strdup("nbNsL3L2L1ch"); */ rmaps_lama_cmd_map = strdup("nbsch"); break; case ORTE_MAPPING_BYBOARD: /* rmaps_lama_cmd_map = strdup("bnNsL3L2L1ch"); */ orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, "by board", "mapping by board not supported by LAMA"); exit_status = ORTE_ERR_NOT_SUPPORTED; goto cleanup; break; case ORTE_MAPPING_BYNUMA: /* rmaps_lama_cmd_map = strdup("NbnsL3L2L1ch"); */ rmaps_lama_cmd_map = strdup("Nbnsch"); break; case ORTE_MAPPING_BYSOCKET: /* rmaps_lama_cmd_map = strdup("sNbnL3L2L1ch"); */ rmaps_lama_cmd_map = strdup("sbnch"); break; case ORTE_MAPPING_BYL3CACHE: rmaps_lama_cmd_map = strdup("L3sNbnL2L1ch"); break; case ORTE_MAPPING_BYL2CACHE: rmaps_lama_cmd_map = strdup("L2sNbnL1ch"); break; case ORTE_MAPPING_BYL1CACHE: rmaps_lama_cmd_map = strdup("L1sNbnch"); break; case ORTE_MAPPING_BYCORE: case ORTE_MAPPING_BYSLOT: /* rmaps_lama_cmd_map = strdup("cL1L2L3sNbnh"); */ rmaps_lama_cmd_map = strdup("csbnh"); break; case ORTE_MAPPING_BYHWTHREAD: /* rmaps_lama_cmd_map = strdup("hcL1L2L3sNbn"); */ rmaps_lama_cmd_map = strdup("hcsbn"); break; case ORTE_MAPPING_RR: orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, "round robin", "mapping by round robin not supported by LAMA"); exit_status = ORTE_ERR_NOT_SUPPORTED; goto cleanup; case ORTE_MAPPING_SEQ: orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, "sequential", "mapping by sequential not supported by LAMA"); exit_status = ORTE_ERR_NOT_SUPPORTED; goto cleanup; case ORTE_MAPPING_BYUSER: orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, "by user", "mapping by user not supported by LAMA"); exit_status = ORTE_ERR_NOT_SUPPORTED; goto cleanup; default: /* * Default is map-by core */ rmaps_lama_cmd_map = strdup("cL1L2L3sNbnh"); break; } } /* * Binding Options */ if( NULL == rmaps_lama_cmd_bind ) { /* * No binding specified, use default */ if( !OPAL_BINDING_POLICY_IS_SET(jdata->map->binding) || !OPAL_BINDING_REQUIRED(opal_hwloc_binding_policy) || OPAL_BIND_TO_NONE == OPAL_GET_BINDING_POLICY(jdata->map->binding) ) { rmaps_lama_cmd_bind = NULL; } switch( OPAL_GET_BINDING_POLICY(jdata->map->binding) ) { case OPAL_BIND_TO_BOARD: /* rmaps_lama_cmd_bind = strdup("1b"); */ orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, "by board", "binding to board not supported by LAMA"); exit_status = ORTE_ERR_NOT_SUPPORTED; goto cleanup; break; case OPAL_BIND_TO_NUMA: rmaps_lama_cmd_bind = strdup("1N"); break; case OPAL_BIND_TO_SOCKET: rmaps_lama_cmd_bind = strdup("1s"); break; case OPAL_BIND_TO_L3CACHE: rmaps_lama_cmd_bind = strdup("1L3"); break; case OPAL_BIND_TO_L2CACHE: rmaps_lama_cmd_bind = strdup("1L2"); break; case OPAL_BIND_TO_L1CACHE: rmaps_lama_cmd_bind = strdup("1L1"); break; case OPAL_BIND_TO_CORE: rmaps_lama_cmd_bind = strdup("1c"); break; case OPAL_BIND_TO_HWTHREAD: rmaps_lama_cmd_bind = strdup("1h"); break; case OPAL_BIND_TO_CPUSET: orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, "by CPU set", "binding to CPU set not supported by LAMA"); exit_status = ORTE_ERR_NOT_SUPPORTED; goto cleanup; break; default: rmaps_lama_cmd_bind = NULL; break; } } /* * Ordering (a.k.a. Ranking) Options */ if( NULL == rmaps_lama_cmd_ordering ) { /* orte_rmaps_base.ranking */ switch( ORTE_GET_RANKING_POLICY(jdata->map->ranking) ) { case ORTE_RANK_BY_SLOT: rmaps_lama_cmd_ordering = strdup("s"); break; case ORTE_RANK_BY_NODE: case ORTE_RANK_BY_NUMA: case ORTE_RANK_BY_SOCKET: case ORTE_RANK_BY_L3CACHE: case ORTE_RANK_BY_L2CACHE: case ORTE_RANK_BY_L1CACHE: case ORTE_RANK_BY_CORE: case ORTE_RANK_BY_HWTHREAD: rmaps_lama_cmd_ordering = strdup("n"); break; case ORTE_RANK_BY_BOARD: /* rmaps_lama_cmd_ordering = strdup("n"); */ orte_show_help("help-orte-rmaps-lama.txt", "invalid ordering option", true, "by board", "ordering by board not supported by LAMA"); exit_status = ORTE_ERR_NOT_SUPPORTED; goto cleanup; break; default: rmaps_lama_cmd_ordering = strdup("n"); break; } } /* * MPPR */ if( NULL == rmaps_lama_cmd_mppr ) { /* * The ppr is given in the map */ if( NULL != jdata->map->ppr) { rmaps_lama_cmd_mppr = rmaps_lama_covert_ppr(jdata->map->ppr); } } /* * Oversubscription */ if( ORTE_MAPPING_NO_OVERSUBSCRIBE & ORTE_GET_MAPPING_DIRECTIVE(jdata->map->mapping) ) { rmaps_lama_can_oversubscribe = false; } else { rmaps_lama_can_oversubscribe = true; } /* * Display revised values */ opal_output_verbose(5, orte_rmaps_base_framework.framework_output, "mca:rmaps:lama: Revised Parameters -----"); opal_output_verbose(5, orte_rmaps_base_framework.framework_output, "mca:rmaps:lama: Map : %s", rmaps_lama_cmd_map); opal_output_verbose(5, orte_rmaps_base_framework.framework_output, "mca:rmaps:lama: Bind : %s", rmaps_lama_cmd_bind); opal_output_verbose(5, orte_rmaps_base_framework.framework_output, "mca:rmaps:lama: MPPR : %s", rmaps_lama_cmd_mppr); opal_output_verbose(5, orte_rmaps_base_framework.framework_output, "mca:rmaps:lama: Order : %s", rmaps_lama_cmd_ordering); cleanup: return exit_status; } static char * rmaps_lama_covert_ppr(char * given_ppr) { return strdup(given_ppr); } int rmaps_lama_parse_mapping(char *layout, rmaps_lama_level_type_t **layout_types, rmaps_lama_level_type_t **layout_types_sorted, int *num_types) { int exit_status = ORTE_SUCCESS; char param[3]; int i, j, len; bool found_req_param_n = false; bool found_req_param_h = false; bool found_req_param_bind = false; /* * Sanity Check: * There is no default layout, so if we get here and nothing is specified * then this is an error. */ if( NULL == layout ) { orte_show_help("help-orte-rmaps-lama.txt", "internal error", true, "rmaps_lama_parse_mapping", "internal error 1"); return ORTE_ERROR; } *num_types = 0; /* * Extract and convert all the keys */ len = strlen(layout); for(i = 0; i < len; ++i) { /* * L1 : L1 Cache * L2 : L2 Cache * L3 : L3 Cache */ if( layout[i] == 'L' ) { param[0] = layout[i]; ++i; /* * Check for 2 characters */ if( i >= len ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, layout, "cache level missing number"); exit_status = ORTE_ERROR; goto cleanup; } param[1] = layout[i]; param[2] = '\0'; } /* * n : Machine * b : Board * s : Socket * c : Core * h : Hardware Thread * N : NUMA Node */ else { param[0] = layout[i]; param[1] = '\0'; } /* * Append level */ *num_types += 1; *layout_types = (rmaps_lama_level_type_t*)realloc(*layout_types, sizeof(rmaps_lama_level_type_t) * (*num_types)); (*layout_types)[(*num_types)-1] = lama_type_str_to_enum(param); } /* * Check for duplicates and unknowns * Copy to sorted list */ *layout_types_sorted = (rmaps_lama_level_type_t*)malloc(sizeof(rmaps_lama_level_type_t) * (*num_types)); for( i = 0; i < *num_types; ++i ) { /* * Copy for later sorting */ (*layout_types_sorted)[i] = (*layout_types)[i]; /* * Look for unknown and unsupported options */ if( LAMA_LEVEL_UNKNOWN <= (*layout_types)[i] ) { char *msg; asprintf(&msg, "unknown mapping level at position %d", i + 1); orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, layout, msg); free(msg); exit_status = ORTE_ERROR; goto cleanup; } if( LAMA_LEVEL_MACHINE == (*layout_types)[i] ) { found_req_param_n = true; } if( LAMA_LEVEL_PU == (*layout_types)[i] ) { found_req_param_h = true; } if( lama_binding_level == (*layout_types)[i] ) { found_req_param_bind = true; } /* * Look for duplicates */ for( j = i+1; j < *num_types; ++j ) { if( (*layout_types)[i] == (*layout_types)[j] ) { char *msg; asprintf(&msg, "duplicate mapping levels at position %d and %d", i + 1, j + 1); orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, layout, msg); free(msg); exit_status = ORTE_ERROR; goto cleanup; } } } /* * The user is required to specify at least the: * - machine * - hardware thread (needed for lower bound binding) JJH: We should be able to lift this... * - binding layer (need it to stride the mapping) * Only print the error message once, for brevity. */ if( !found_req_param_n ) { char *msg; asprintf(&msg, "missing required 'n' mapping token"); orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, layout, msg); free(msg); exit_status = ORTE_ERROR; goto cleanup; } else if(!found_req_param_h) { char *msg; asprintf(&msg, "missing required 'h' mapping token"); orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, layout, msg); free(msg); exit_status = ORTE_ERROR; goto cleanup; } else if (!found_req_param_bind) { char *msg; asprintf(&msg, "missing required mapping token for the current binding level"); orte_show_help("help-orte-rmaps-lama.txt", "invalid mapping option", true, layout, msg); free(msg); exit_status = ORTE_ERROR; goto cleanup; } /* * Sort the items */ qsort((*layout_types_sorted ), (*num_types), sizeof(int), lama_parse_int_sort); cleanup: return exit_status; } int rmaps_lama_parse_binding(char *layout, rmaps_lama_level_type_t *binding_level, int *num_types) { int exit_status = ORTE_SUCCESS; char param[3]; char num[MAX_BIND_DIGIT_LEN]; int i, n, p, len; /* * Default: If nothing specified * - Bind to machine */ if( NULL == layout ) { *binding_level = LAMA_LEVEL_MACHINE; *num_types = 1; return ORTE_SUCCESS; } *num_types = 0; /* * Extract and convert all the keys */ len = strlen(layout); n = 0; p = 0; for(i = 0; i < len; ++i) { /* * Must start with a digit */ if( isdigit(layout[i]) ) { /* * Check: Digits must come first */ if( p != 0 ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, layout, "missing digit(s) before binding level token"); exit_status = ORTE_ERROR; goto cleanup; } num[n] = layout[i]; ++n; /* * Check: Exceed bound of number of digits */ if( n >= MAX_BIND_DIGIT_LEN ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, layout, "too many digits"); exit_status = ORTE_ERROR; goto cleanup; } } /* * Extract the level */ else { /* * Check: Digits must come first */ if( n == 0 ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, layout, "missing digit(s) before binding level token"); exit_status = ORTE_ERROR; goto cleanup; } /* * Check: Only one level allowed */ if( p != 0 ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, layout, "only one binding level may be specified"); exit_status = ORTE_ERROR; goto cleanup; } /* * L1 : L1 Cache * L2 : L2 Cache * L3 : L3 Cache */ if( layout[i] == 'L' ) { param[0] = layout[i]; ++i; /* * Check for 2 characters */ if( i >= len ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, layout, "only one binding level may be specified"); exit_status = ORTE_ERROR; goto cleanup; } param[1] = layout[i]; p = 2; } /* * n : Machine * b : Board * s : Socket * c : Core * h : Hardware Thread * N : NUMA Node */ else { param[0] = layout[i]; p = 1; } param[p] = '\0'; } } /* * Check that the level was specified */ if( p == 0 ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, layout, "binding specification is empty"); exit_status = ORTE_ERROR; goto cleanup; } num[n] = '\0'; *binding_level = lama_type_str_to_enum(param); *num_types = atoi(num); /* * Check for unknown level */ if( LAMA_LEVEL_UNKNOWN <= *binding_level ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid binding option", true, layout, "unknown binding level"); exit_status = ORTE_ERROR; goto cleanup; } cleanup: return exit_status; } int rmaps_lama_parse_mppr(char *layout, rmaps_lama_level_info_t **mppr_levels, int *num_types) { int exit_status = ORTE_SUCCESS; char param[3]; char num[MAX_BIND_DIGIT_LEN]; char **argv = NULL; int argc = 0; int i, j, len; int p, n; /* * Default: Unrestricted allocation * 'oversubscribe' flag accounted for elsewhere */ if( NULL == layout ) { *mppr_levels = NULL; *num_types = 0; return ORTE_SUCCESS; } *num_types = 0; /* * Split by ',' * <#:level>,<#:level>,... */ argv = opal_argv_split(layout, ','); argc = opal_argv_count(argv); for(j = 0; j < argc; ++j) { /* * Parse <#:level> */ len = strlen(argv[j]); n = 0; p = 0; for(i = 0; i < len; ++i) { /* * Skip the ':' separator and whitespace */ if( argv[j][i] == ':' || isblank(argv[j][i])) { continue; } /* * Must start with a digit */ else if( isdigit(argv[j][i]) ) { /* * Check: Digits must come first */ if( p != 0 ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid mppr option", true, layout, "missing digit(s) before resource specification"); exit_status = ORTE_ERROR; goto cleanup; } num[n] = argv[j][i]; ++n; /* * Check: Exceed bound of number of digits */ if( n >= MAX_BIND_DIGIT_LEN ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid mppr option", true, layout, "too many digits"); exit_status = ORTE_ERROR; goto cleanup; } } /* * Extract the level */ else { /* * Check: Digits must come first */ if( n == 0 ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid mppr option", true, layout, "missing digit(s) before resource specification"); exit_status = ORTE_ERROR; goto cleanup; } /* * Check: Only one level allowed */ if( p != 0 ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid mppr option", true, layout, "only one resource type may be listed per specification"); exit_status = ORTE_ERROR; goto cleanup; } /* * L1 : L1 Cache * L2 : L2 Cache * L3 : L3 Cache */ if( argv[j][i] == 'L' ) { param[0] = argv[j][i]; ++i; /* * Check for 2 characters */ if( i >= len ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid mppr option", true, layout, "cache level missing number"); exit_status = ORTE_ERROR; goto cleanup; } param[1] = argv[j][i]; p = 2; } /* * n : Machine * b : Board * s : Socket * c : Core * h : Hardware Thread * N : NUMA Node */ else { param[0] = argv[j][i]; p = 1; } param[p] = '\0'; } } /* * Whitespace, just skip */ if( n == 0 && p == 0 ) { continue; } /* * Check that the level was specified */ if( p == 0 ) { orte_show_help("help-orte-rmaps-lama.txt", "invalid mppr option", true, layout, "resource type not specified"); exit_status = ORTE_ERROR; goto cleanup; } num[n] = '\0'; /* * Append level */ *num_types += 1; *mppr_levels = (rmaps_lama_level_info_t*)realloc(*mppr_levels, sizeof(rmaps_lama_level_info_t) * (*num_types)); (*mppr_levels)[(*num_types)-1].type = lama_type_str_to_enum(param); (*mppr_levels)[(*num_types)-1].max_resources = atoi(num); } /* * Check for duplicates and unknowns */ for( i = 0; i < *num_types; ++i ) { /* * Look for unknown and unsupported options */ if( LAMA_LEVEL_UNKNOWN <= (*mppr_levels)[i].type ) { char *msg; asprintf(&msg, "unknown resource type at position %d", i + 1); orte_show_help("help-orte-rmaps-lama.txt", "invalid mppr option", true, layout, msg); free(msg); exit_status = ORTE_ERROR; goto cleanup; } /* * Look for duplicates */ for( j = i+1; j < *num_types; ++j ) { if( (*mppr_levels)[i].type == (*mppr_levels)[j].type ) { char *msg; asprintf(&msg, "duplicate resource tpyes at position %d and %d", i + 1, j + 1); orte_show_help("help-orte-rmaps-lama.txt", "invalid mppr option", true, layout, msg); free(msg); exit_status = ORTE_ERROR; goto cleanup; } } } cleanup: if( NULL != argv ) { opal_argv_free(argv); argv = NULL; } return exit_status; } int rmaps_lama_parse_ordering(char *layout, rmaps_lama_order_type_t *order) { /* * Default: Natural ordering */ if( NULL == layout ) { *order = LAMA_ORDER_NATURAL; return ORTE_SUCCESS; } /* * Sequential Ordering */ if( 0 == strncmp(layout, "s", strlen("s")) || 0 == strncmp(layout, "S", strlen("S")) ) { *order = LAMA_ORDER_SEQ; } /* * Natural Ordering */ else if( 0 == strncmp(layout, "n", strlen("n")) || 0 == strncmp(layout, "N", strlen("N")) ) { *order = LAMA_ORDER_NATURAL; } /* * Check for unknown options */ else { orte_show_help("help-orte-rmaps-lama.txt", "invalid ordering option", true, "unsupported ordering option", layout); return ORTE_ERROR; } return ORTE_SUCCESS; } bool rmaps_lama_ok_to_prune_level(rmaps_lama_level_type_t level) { int i; for( i = 0; i < lama_mapping_num_layouts; ++i ) { if( level == lama_mapping_layout[i] ) { return false; } } return true; } /********************************* * Support Functions *********************************/ static int lama_parse_int_sort(const void *a, const void *b) { int left = *((int*)a); int right = *((int*)b); if( left < right ) { return -1; } else if( left > right ) { return 1; } else { return 0; } }