@@ -60,6 +60,12 @@ static char *ofi_progress_mode;
6060static bool disable_sep ;
6161static int mca_btl_ofi_init_device (struct fi_info * info );
6262
63+ /* qsort comparator for an array of domain name strings */
64+ static int domain_name_compare (const void * a , const void * b )
65+ {
66+ return strcmp (* (const char * const * ) a , * (const char * const * ) b );
67+ }
68+
6369/* validate information returned from fi_getinfo().
6470 * return OPAL_ERROR if we dont have what we need. */
6571static int validate_info (struct fi_info * info , uint64_t required_caps , char * * include_list ,
@@ -244,6 +250,8 @@ static int mca_btl_ofi_component_close(void)
244250{
245251 int ret ;
246252 ret = opal_common_ofi_close ();
253+ free (mca_btl_ofi_component .modules );
254+ mca_btl_ofi_component .modules = NULL ;
247255 /* If we don't sleep, sockets provider freaks out. Ummm this is a scary comment */
248256 sleep (1 );
249257 return ret ;
@@ -285,7 +293,8 @@ static mca_btl_base_module_t **mca_btl_ofi_component_init(int *num_btl_modules,
285293 return NULL ;
286294 }
287295
288- struct fi_info * info , * info_list = NULL , * selected_info = NULL ;
296+ struct fi_info * info , * info_list = NULL ;
297+ const char * * unique_domains = NULL ;
289298 struct fi_info hints = {0 };
290299 struct fi_ep_attr ep_attr = {0 };
291300 struct fi_rx_attr rx_attr = {0 };
@@ -447,40 +456,130 @@ static mca_btl_base_module_t **mca_btl_ofi_component_init(int *num_btl_modules,
447456
448457 info = info_list ;
449458
450- while (info ) {
451- rc = validate_info (info , required_caps , include_list , exclude_list );
452- if (OPAL_SUCCESS == rc ) {
453- /* Device passed sanity check, let's make a module.
454- *
455- * The initial fi_getinfo() call will return a list of providers
456- * available for this process. once a provider is selected from the
457- * list, we will cycle through the remaining list to identify NICs
458- * serviced by this provider, and try to pick one on the same NUMA
459- * node as this process. If there are no NICs on the same NUMA node,
460- * we pick one in a manner which allows all ranks to make balanced
461- * use of available NICs on the system.
462- *
463- * Most providers give a separate fi_info object for each NIC,
464- * however some may have multiple info objects with different
465- * attributes for the same NIC. The initial provider attributes
466- * are used to ensure that all NICs we return provide the same
467- * capabilities as the initial one.
468- */
469- selected_info = opal_common_ofi_select_provider (info , & opal_process_info );
470- rc = mca_btl_ofi_init_device (selected_info );
471- if (OPAL_SUCCESS == rc ) {
472- info = selected_info ;
459+ /* Count unique NIC domains for the selected provider to determine how many
460+ * modules this process should create. This avoids creating resources that
461+ * will never be used. We match by both provider name and domain name to
462+ * avoid double-counting entries that share a physical NIC but differ in
463+ * provider variant (e.g., efa vs efa-direct) or attributes. */
464+ int num_nics = 0 ;
465+ const char * first_prov = NULL ;
466+ if (resource_count > 0 ) {
467+ unique_domains = (const char * * ) calloc (resource_count , sizeof (const char * ));
468+ }
469+ {
470+ struct fi_info * tmp = info_list ;
471+ while (tmp && NULL != unique_domains ) {
472+ if (OPAL_SUCCESS == validate_info (tmp , required_caps , include_list , exclude_list )) {
473+ const char * p = (NULL != tmp -> fabric_attr ) ? tmp -> fabric_attr -> prov_name : NULL ;
474+ const char * d = (NULL != tmp -> domain_attr ) ? tmp -> domain_attr -> name : NULL ;
475+ if (NULL == first_prov ) first_prov = p ;
476+ if (NULL != first_prov && NULL != p && 0 == strcmp (first_prov , p ) && NULL != d ) {
477+ /* Only count unique domain names */
478+ bool dup = false;
479+ for (int i = 0 ; i < num_nics ; i ++ ) {
480+ if (0 == strcmp (unique_domains [i ], d )) { dup = true; break ; }
481+ }
482+ if (!dup ) {
483+ unique_domains [num_nics ] = d ;
484+ num_nics ++ ;
485+ }
486+ }
487+ }
488+ tmp = tmp -> next ;
489+ }
490+ }
491+ /* Sort the unique domain names so the list order is deterministic and
492+ * identical on every process regardless of how the provider ordered
493+ * its fi_getinfo() results. */
494+ if (1 < num_nics ) {
495+ qsort (unique_domains , num_nics , sizeof (unique_domains [0 ]),
496+ domain_name_compare );
497+ }
498+
499+ /* Distribute NICs evenly across local processes */
500+ int num_local_procs = (int )(opal_process_info .num_local_peers + 1 );
501+ int modules_per_proc = (num_nics > num_local_procs ) ? (num_nics / num_local_procs ) : 1 ;
502+
503+ /* Each local process takes a disjoint, contiguous slice of the sorted
504+ * unique NIC (domain) list so that local processes do not collapse
505+ * onto the same NICs. The list order is identical on all processes on
506+ * a node, so slices are disjoint whenever there are at least as many
507+ * NICs as local processes. */
508+ int slice_start = 0 ;
509+ if (0 < num_nics ) {
510+ slice_start = ((int ) opal_process_info .my_local_rank * modules_per_proc ) % num_nics ;
511+ }
512+
513+ /* Allocate the modules array dynamically based on actual need */
514+ mca_btl_ofi_component .modules = (mca_btl_ofi_module_t * * )
515+ calloc (modules_per_proc , sizeof (mca_btl_ofi_module_t * ));
516+ if (NULL == mca_btl_ofi_component .modules ) {
517+ goto out ;
518+ }
519+ mca_btl_ofi_component .modules_allocated = modules_per_proc ;
520+
521+ /* Create one module per NIC (domain) in this process' slice of the
522+ * unique-domain list. Pin to a single provider (the first valid one)
523+ * so all modules share one address format. */
524+ for (int mi = 0 ; mi < modules_per_proc && 0 < num_nics ; mi ++ ) {
525+ const char * want = unique_domains [(slice_start + mi ) % num_nics ];
526+ for (info = info_list ; NULL != info ; info = info -> next ) {
527+ if (OPAL_SUCCESS != validate_info (info , required_caps , include_list , exclude_list )) {
528+ continue ;
529+ }
530+ const char * prov = (NULL != info -> fabric_attr )
531+ ? info -> fabric_attr -> prov_name : NULL ;
532+ const char * d = (NULL != info -> domain_attr ) ? info -> domain_attr -> name : NULL ;
533+ if (NULL != first_prov && NULL != prov && NULL != d
534+ && 0 == strcmp (first_prov , prov ) && 0 == strcmp (want , d )) {
535+ (void ) mca_btl_ofi_init_device (info );
473536 break ;
474537 }
475538 }
476- info = info -> next ;
477539 }
478540
479- if (NULL == info ) {
541+ if (0 == mca_btl_ofi_component . module_count ) {
480542 BTL_VERBOSE (("No provider is selected" ));
481543 goto out ;
482544 }
483545
546+ /* Publish all module endpoint names in a single modex blob so peers can
547+ * pair modules by index. Layout: uint32 nmodules, then per module:
548+ * uint32 namelen, namelen bytes. */
549+ {
550+ size_t total = sizeof (uint32_t );
551+ for (int mi = 0 ; mi < mca_btl_ofi_component .module_count ; mi ++ ) {
552+ total += sizeof (uint32_t ) + mca_btl_ofi_component .modules [mi ]-> ep_namelen ;
553+ }
554+ uint8_t * blob = (uint8_t * ) malloc (total );
555+ if (NULL == blob ) {
556+ BTL_ERROR (("failed to allocate modex blob" ));
557+ goto out ;
558+ }
559+ uint8_t * p = blob ;
560+ uint32_t nm = (uint32_t ) mca_btl_ofi_component .module_count ;
561+ memcpy (p , & nm , sizeof (uint32_t ));
562+ p += sizeof (uint32_t );
563+ for (int mi = 0 ; mi < mca_btl_ofi_component .module_count ; mi ++ ) {
564+ uint32_t nl = (uint32_t ) mca_btl_ofi_component .modules [mi ]-> ep_namelen ;
565+ memcpy (p , & nl , sizeof (uint32_t ));
566+ p += sizeof (uint32_t );
567+ memcpy (p , mca_btl_ofi_component .modules [mi ]-> ep_name , nl );
568+ p += nl ;
569+ }
570+ OPAL_MODEX_SEND (rc , PMIX_GLOBAL , & mca_btl_ofi_component .super .btl_version , blob , total );
571+ free (blob );
572+ if (OPAL_SUCCESS != rc ) {
573+ BTL_ERROR (("modex send failed" ));
574+ goto out ;
575+ }
576+ /* ep_name copies no longer needed after packing into blob */
577+ for (int mi = 0 ; mi < mca_btl_ofi_component .module_count ; mi ++ ) {
578+ free (mca_btl_ofi_component .modules [mi ]-> ep_name );
579+ mca_btl_ofi_component .modules [mi ]-> ep_name = NULL ;
580+ }
581+ }
582+
484583 /* pass module array back to caller */
485584 base_modules = calloc (mca_btl_ofi_component .module_count , sizeof (* base_modules ));
486585 if (NULL == base_modules ) {
@@ -496,6 +595,21 @@ static mca_btl_base_module_t **mca_btl_ofi_component_init(int *num_btl_modules,
496595 * num_btl_modules = mca_btl_ofi_component .module_count ;
497596
498597out :
598+ if (NULL == base_modules ) {
599+ /* Initialization failed after some modules may already have been
600+ * created: finalize them so we do not leak libfabric resources. */
601+ for (int mi = 0 ; mi < mca_btl_ofi_component .module_count ; mi ++ ) {
602+ if (NULL != mca_btl_ofi_component .modules [mi ]) {
603+ (void ) mca_btl_ofi_finalize (
604+ (mca_btl_base_module_t * ) mca_btl_ofi_component .modules [mi ]);
605+ }
606+ }
607+ mca_btl_ofi_component .module_count = 0 ;
608+ free (mca_btl_ofi_component .modules );
609+ mca_btl_ofi_component .modules = NULL ;
610+ mca_btl_ofi_component .modules_allocated = 0 ;
611+ }
612+ free (unique_domains );
499613 if (include_list ) {
500614 opal_argv_free (include_list );
501615 }
@@ -516,7 +630,7 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
516630 size_t namelen ;
517631 size_t num_contexts_to_create ;
518632
519- char * linux_device_name ;
633+ char * domain_name = NULL ;
520634 void * ep_name = NULL ;
521635
522636 struct fi_info * ofi_info ;
@@ -564,21 +678,25 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
564678 fi_strerror (- rc )));
565679 }
566680
567- linux_device_name = info -> domain_attr -> name ;
681+ domain_name = strdup (info -> domain_attr -> name );
682+ if (NULL == domain_name ) {
683+ BTL_ERROR (("failed to duplicate domain name" ));
684+ goto fail ;
685+ }
568686 BTL_VERBOSE (
569- ("initializing dev:%s provider:%s" , linux_device_name , info -> fabric_attr -> prov_name ));
687+ ("initializing dev:%s provider:%s" , domain_name , info -> fabric_attr -> prov_name ));
570688
571689 /* fabric */
572690 rc = opal_common_ofi_fi_fabric (ofi_info -> fabric_attr , & fabric );
573691 if (0 != rc ) {
574- BTL_VERBOSE (("%s failed fi_fabric with err=%s" , linux_device_name , fi_strerror (- rc )));
692+ BTL_VERBOSE (("%s failed fi_fabric with err=%s" , domain_name , fi_strerror (- rc )));
575693 goto fail ;
576694 }
577695
578696 /* domain */
579697 rc = opal_common_ofi_fi_domain (fabric , ofi_info , & domain );
580698 if (0 != rc ) {
581- BTL_VERBOSE (("%s failed fi_domain with err=%s" , linux_device_name , fi_strerror (- rc )));
699+ BTL_VERBOSE (("%s failed fi_domain with err=%s" , domain_name , fi_strerror (- rc )));
582700 goto fail ;
583701 }
584702
@@ -591,7 +709,7 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
591709 av_attr .type = FI_AV_MAP ;
592710 rc = fi_av_open (domain , & av_attr , & av , NULL );
593711 if (0 != rc ) {
594- BTL_VERBOSE (("%s failed fi_av_open with err=%s" , linux_device_name , fi_strerror (- rc )));
712+ BTL_VERBOSE (("%s failed fi_av_open with err=%s" , domain_name , fi_strerror (- rc )));
595713 goto fail ;
596714 }
597715
@@ -616,7 +734,7 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
616734 rc = fi_scalable_ep (domain , ofi_info , & ep , NULL );
617735 if (0 != rc ) {
618736 BTL_VERBOSE (
619- ("%s failed fi_scalable_ep with err=%s" , linux_device_name , fi_strerror (- rc )));
737+ ("%s failed fi_scalable_ep with err=%s" , domain_name , fi_strerror (- rc )));
620738 goto fail ;
621739 }
622740
@@ -639,7 +757,7 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
639757
640758 rc = fi_endpoint (domain , ofi_info , & ep , NULL );
641759 if (0 != rc ) {
642- BTL_VERBOSE (("%s failed fi_endpoint with err=%s" , linux_device_name , fi_strerror (- rc )));
760+ BTL_VERBOSE (("%s failed fi_endpoint with err=%s" , domain_name , fi_strerror (- rc )));
643761 goto fail ;
644762 }
645763
@@ -658,7 +776,7 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
658776 /* enable the endpoint for using */
659777 rc = fi_enable (ep );
660778 if (0 != rc ) {
661- BTL_VERBOSE (("%s failed fi_enable with err=%s" , linux_device_name , fi_strerror (- rc )));
779+ BTL_VERBOSE (("%s failed fi_enable with err=%s" , domain_name , fi_strerror (- rc )));
662780 goto fail ;
663781 }
664782
@@ -669,7 +787,7 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
669787 module -> domain = domain ;
670788 module -> av = av ;
671789 module -> ofi_endpoint = ep ;
672- module -> linux_device_name = linux_device_name ;
790+ module -> domain_name = domain_name ;
673791 module -> outstanding_rdma = 0 ;
674792 module -> use_virt_addr = false;
675793 module -> use_fi_mr_bind = false;
@@ -704,7 +822,7 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
704822 & ep_name ,
705823 & namelen );
706824 if (OPAL_SUCCESS != rc ) {
707- BTL_VERBOSE (("%s failed opal_common_ofi_fi_getname with err=%d" , linux_device_name , rc ));
825+ BTL_VERBOSE (("%s failed opal_common_ofi_fi_getname with err=%d" , domain_name , rc ));
708826 goto fail ;
709827 }
710828
@@ -720,12 +838,12 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
720838 }
721839 }
722840
723- /* post our endpoint name so peer can use it to connect to us */
724- OPAL_MODEX_SEND (rc , PMIX_GLOBAL , & mca_btl_ofi_component .super .btl_version , ep_name , namelen );
725- mca_btl_ofi_component .namelen = namelen ;
726- free (ep_name );
841+ /* Save endpoint name on the module; modex send happens after all modules are created */
842+ module -> ep_name = ep_name ;
843+ module -> ep_namelen = namelen ;
727844
728845 /* add this module to the list */
846+ module -> module_index = * module_count ;
729847 mca_btl_ofi_component .modules [(* module_count )++ ] = module ;
730848
731849 return OPAL_SUCCESS ;
@@ -765,6 +883,7 @@ static int mca_btl_ofi_init_device(struct fi_info *info)
765883 if (NULL != fabric ) {
766884 opal_common_ofi_fabric_release (fabric );
767885 }
886+ free (domain_name );
768887 free (module );
769888
770889 if (NULL != ep_name ) {
0 commit comments