1 /*
2  * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
3  *                         University Research and Technology
4  *                         Corporation.  All rights reserved.
5  * Copyright (c) 2004-2005 The University of Tennessee and The University
6  *                         of Tennessee Research Foundation.  All rights
7  *                         reserved.
8  * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9  *                         University of Stuttgart.  All rights reserved.
10  * Copyright (c) 2004-2005 The Regents of the University of California.
11  *                         All rights reserved.
12  * Copyright (c) 2008      Sun Microsystems, Inc.  All rights reserved.
13  * Copyright (c) 2008-2011 University of Houston. All rights reserved.
14  * Copyright (c) 2015      Research Organization for Information Science
15  *                         and Technology (RIST). All rights reserved.
16  * Copyright (c) 2016-2017 IBM Corporation. All rights reserved.
17  * $COPYRIGHT$
18  *
19  * Additional copyrights may follow
20  *
21  * $HEADER$
22  */
23 
24 #include "ompi_config.h"
25 
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #include "mpi.h"
31 #include "ompi/file/file.h"
32 #include "opal/util/argv.h"
33 #include "opal/util/output.h"
34 #include "opal/util/info.h"
35 #include "opal/class/opal_list.h"
36 #include "opal/class/opal_object.h"
37 #include "ompi/mca/mca.h"
38 #include "opal/mca/base/base.h"
39 #include "ompi/mca/io/io.h"
40 #include "ompi/mca/io/base/base.h"
41 #include "ompi/mca/io/base/io_base_request.h"
42 #include "ompi/mca/fs/fs.h"
43 #include "ompi/mca/fs/base/base.h"
44 #include "ompi/mca/fcoll/fcoll.h"
45 #include "ompi/mca/fcoll/base/base.h"
46 #include "ompi/mca/fbtl/fbtl.h"
47 #include "ompi/mca/fbtl/base/base.h"
48 #include "ompi/mca/sharedfp/sharedfp.h"
49 #include "ompi/mca/sharedfp/base/base.h"
50 
51 opal_mutex_t ompi_mpi_ompio_bootstrap_mutex = OPAL_MUTEX_STATIC_INIT;
52 /*
53  * Local types
54  */
55 struct avail_io_t {
56     opal_list_item_t super;
57 
58     mca_io_base_version_t ai_version;
59 
60     int ai_priority;
61     mca_io_base_components_t ai_component;
62     mca_io_base_modules_t ai_module;
63     struct mca_io_base_file_t *ai_module_data;
64 };
65 typedef struct avail_io_t avail_io_t;
66 
67 /*
68  * Local functions
69  */
70 static opal_list_t *check_components(opal_list_t *components,
71                                      ompi_file_t *file,
72                                      char **names, int num_names);
73 static avail_io_t *check_one_component(ompi_file_t *file,
74                                        const mca_base_component_t *component);
75 
76 static avail_io_t *query(const mca_base_component_t *component,
77                          ompi_file_t *file);
78 static avail_io_t *query_2_0_0(const mca_io_base_component_2_0_0_t *io_component,
79                                ompi_file_t *file);
80 
81 static void unquery(avail_io_t *avail, ompi_file_t *file);
82 
83 static int module_init(ompi_file_t *file);
84 
85 
86 /*
87  * Stuff for the OBJ interface
88  */
89 static OBJ_CLASS_INSTANCE(avail_io_t, opal_list_item_t, NULL, NULL);
90 
91 
92 /*
93  * This function is called at the initialization time of every
94  * file.  It is used to select which io component will be
95  * active for a given file.
96  */
mca_io_base_file_select(ompi_file_t * file,mca_base_component_t * preferred)97 int mca_io_base_file_select(ompi_file_t *file,
98                             mca_base_component_t *preferred)
99 {
100     int err;
101     char *str;
102     opal_list_t *selectable;
103     opal_list_item_t *item;
104     avail_io_t *avail, selected;
105 
106     /* Announce */
107 
108     opal_output_verbose(10, ompi_io_base_framework.framework_output,
109                         "io:base:file_select: new file: %s",
110                         file->f_filename);
111 
112     /* Initialize all the relevant pointers, since they're used as
113        sentinel values */
114 
115     file->f_io_version = MCA_IO_BASE_V_NONE;
116     file->f_io_selected_data = NULL;
117 
118     /* Compute the intersection of all of my available components with
119        the components from all the other processes in this file */
120 
121     /* JMS CONTINUE HERE */
122 
123     /* See if a preferred component was provided.  If so, try to
124        select it.  If we don't succeed, fall through and do a normal
125        selection. */
126 
127     err = OMPI_ERROR;
128     if (NULL != preferred) {
129         str = &(preferred->mca_component_name[0]);
130 
131         opal_output_verbose(10, ompi_io_base_framework.framework_output,
132                             "io:base:file_select: Checking preferred module: %s",
133                             str);
134         selectable = check_components(&ompi_io_base_framework.framework_components,
135                                       file, &str, 1);
136 
137         /* If we didn't get a preferred module, then call again
138            without a preferred module.  This makes the logic below
139            dramatically simpler. */
140 
141         if (NULL == selectable) {
142             return mca_io_base_file_select(file, NULL);
143         }
144 
145         /* We only fall through here if we were able to select one of
146            the preferred modules */
147     }
148 
149     /* Nope -- a specific [set of] component[s] was not requested.  Go
150        check them all. */
151 
152     else {
153         opal_output_verbose(10, ompi_io_base_framework.framework_output,
154                             "io:base:file_select: Checking all available modules");
155         selectable = check_components(&ompi_io_base_framework.framework_components,
156                                       file, NULL, 0);
157     }
158 
159     /* Upon return from the above, the modules list will contain the
160        list of modules that returned (priority >= 0).  If we have no
161        io modules available, it's an error */
162 
163     if (NULL == selectable) {
164         /* There's no modules available.  Doh! */
165         /* show_help */
166         return OMPI_ERROR;
167     }
168 
169     /* Do some kind of collective operation to find a module that
170        everyone has available */
171 
172 #if 1
173     /* For the moment, just take the top module off the list */
174     /* MSC actually take the buttom */
175     item = opal_list_remove_last(selectable);
176     avail = (avail_io_t *) item;
177     selected = *avail;
178     OBJ_RELEASE(avail);
179 #else
180     /* JMS CONTINUE HERE */
181 #endif
182 
183     /* Everything left in the selectable list is therefore unwanted,
184        and we call their unquery() method (because they all had
185        query() invoked, but will never have init() invoked in this
186        scope). */
187 
188     for (item = opal_list_remove_first(selectable); item != NULL;
189          item = opal_list_remove_first(selectable)) {
190         avail = (avail_io_t *) item;
191         unquery(avail, file);
192         OBJ_RELEASE(item);
193     }
194     OBJ_RELEASE(selectable);
195 
196     /* Save the pointers of the selected module on the ompi_file_t */
197 
198     file->f_io_version = selected.ai_version;
199     file->f_io_selected_component = selected.ai_component;
200     file->f_io_selected_module = selected.ai_module;
201     file->f_io_selected_data = selected.ai_module_data;
202 
203     if (!strcmp (selected.ai_component.v2_0_0.io_version.mca_component_name,
204                  "ompio")) {
205         int ret;
206 
207         opal_mutex_lock(&ompi_mpi_ompio_bootstrap_mutex);
208         if (OMPI_SUCCESS != (ret = mca_base_framework_open(&ompi_fs_base_framework, 0))) {
209             opal_mutex_unlock(&ompi_mpi_ompio_bootstrap_mutex);
210             return err;
211         }
212         if (OMPI_SUCCESS != (ret = mca_base_framework_open(&ompi_fcoll_base_framework, 0))) {
213             opal_mutex_unlock(&ompi_mpi_ompio_bootstrap_mutex);
214             return err;
215         }
216         if (OMPI_SUCCESS != (ret = mca_base_framework_open(&ompi_fbtl_base_framework, 0))) {
217             opal_mutex_unlock(&ompi_mpi_ompio_bootstrap_mutex);
218             return err;
219         }
220         if (OMPI_SUCCESS != (ret = mca_base_framework_open(&ompi_sharedfp_base_framework, 0))) {
221             opal_mutex_unlock(&ompi_mpi_ompio_bootstrap_mutex);
222             return err;
223         }
224         opal_mutex_unlock(&ompi_mpi_ompio_bootstrap_mutex);
225 
226         if (OMPI_SUCCESS !=
227             (ret = mca_fs_base_find_available(OPAL_ENABLE_PROGRESS_THREADS, 1))) {
228             return err;
229         }
230         if (OMPI_SUCCESS !=
231             (ret = mca_fcoll_base_find_available(OPAL_ENABLE_PROGRESS_THREADS, 1))) {
232             return err;
233         }
234         if (OMPI_SUCCESS !=
235             (ret = mca_fbtl_base_find_available(OPAL_ENABLE_PROGRESS_THREADS, 1))) {
236             return err;
237         }
238         if (OMPI_SUCCESS !=
239             (ret = mca_sharedfp_base_find_available(OPAL_ENABLE_PROGRESS_THREADS, 1))) {
240             return err;
241         }
242 
243     }
244     /* Finally -- intialize the selected module. */
245 
246     if (OMPI_SUCCESS != (err = module_init(file))) {
247         return err;
248     }
249 
250     /* Announce the winner */
251 
252     opal_output_verbose(10, ompi_io_base_framework.framework_output,
253                         "io:base:file_select: Selected io module %s",
254                         selected.ai_component.v2_0_0.io_version.mca_component_name);
255 
256     return OMPI_SUCCESS;
257 }
258 
avail_io_compare(opal_list_item_t ** itema,opal_list_item_t ** itemb)259 static int avail_io_compare (opal_list_item_t **itema,
260                              opal_list_item_t **itemb)
261 {
262     const avail_io_t *availa = (const avail_io_t *) *itema;
263     const avail_io_t *availb = (const avail_io_t *) *itemb;
264 
265     /* highest component last */
266     if (availa->ai_priority > availb->ai_priority) {
267         return 1;
268     } else if (availa->ai_priority < availb->ai_priority) {
269         return -1;
270     } else {
271         return 0;
272     }
273 }
274 
275 /*
276  * For each component in the list, if it is in the list of names (or
277  * the list of names is NULL), then check and see if it wants to run,
278  * and do the resulting priority comparison.  Make a list of
279  * (component, module) tuples (of type avail_io_t) to be only those
280  * who returned that they want to run, and put them in priority order.
281  */
check_components(opal_list_t * components,ompi_file_t * file,char ** names,int num_names)282 static opal_list_t *check_components(opal_list_t *components,
283                                      ompi_file_t *file,
284                                      char **names, int num_names)
285 {
286     int i;
287     const mca_base_component_t *component;
288     mca_base_component_list_item_t *cli;
289     bool want_to_check;
290     opal_list_t *selectable;
291     avail_io_t *avail;
292 
293     /* Make a list of the components that query successfully */
294 
295     selectable = OBJ_NEW(opal_list_t);
296 
297     /* Scan through the list of components.  This nested loop is
298        O(N^2), but we should never have too many components and/or
299        names, so this *hopefully* shouldn't matter... */
300 
301     OPAL_LIST_FOREACH(cli, components, mca_base_component_list_item_t) {
302         component = cli->cli_component;
303 
304         /* If we have a list of names, scan through it */
305 
306         if (0 == num_names) {
307             want_to_check = true;
308         } else {
309             want_to_check = false;
310             for (i = 0; i < num_names; ++i) {
311                 if (0 == strcmp(names[i], component->mca_component_name)) {
312                     want_to_check = true;
313                 }
314             }
315         }
316 
317         /* If we determined that we want to check this component, then
318            do so */
319 
320         if (want_to_check) {
321             avail = check_one_component(file, component);
322             if (NULL != avail) {
323                 /* Put this item on the list in priority order
324                    (highest priority first).  Should it go first? */
325                 /* MSC actually put it Lowest priority first */
326                 /* NTH sorted later */
327                 opal_list_append(selectable, (opal_list_item_t*)avail);
328             }
329         }
330     }
331 
332     /* If we didn't find any available components, return an error */
333 
334     if (0 == opal_list_get_size(selectable)) {
335         OBJ_RELEASE(selectable);
336         return NULL;
337     }
338 
339     opal_list_sort(selectable, avail_io_compare);
340 
341     /* All done */
342 
343     return selectable;
344 }
345 
346 /*
347  * Check a single component
348  */
check_one_component(ompi_file_t * file,const mca_base_component_t * component)349 static avail_io_t *check_one_component(ompi_file_t *file,
350                                        const mca_base_component_t *component)
351 {
352     avail_io_t *avail;
353 
354     avail = query(component, file);
355     if (NULL != avail) {
356         avail->ai_priority = (avail->ai_priority < 100) ?
357             avail->ai_priority : 100;
358         avail->ai_priority = (avail->ai_priority < 0) ?
359             0 : avail->ai_priority;
360         opal_output_verbose(10, ompi_io_base_framework.framework_output,
361                             "io:base:file_select: component available: %s, priority: %d",
362                             component->mca_component_name,
363                             avail->ai_priority);
364     } else {
365         opal_output_verbose(10, ompi_io_base_framework.framework_output,
366                             "io:base:file_select: component not available: %s",
367                             component->mca_component_name);
368     }
369 
370     return avail;
371 }
372 
373 
374 /**************************************************************************
375  * Query functions
376  **************************************************************************/
377 
378 /*
379  * Take any version of a io module, query it, and return the right
380  * module struct
381  */
query(const mca_base_component_t * component,ompi_file_t * file)382 static avail_io_t *query(const mca_base_component_t *component,
383                          ompi_file_t *file)
384 {
385     const mca_io_base_component_2_0_0_t *ioc_200;
386 
387     /* MCA version check */
388 
389     if (MCA_BASE_VERSION_MAJOR == component->mca_major_version &&
390         MCA_BASE_VERSION_MINOR == component->mca_minor_version &&
391         MCA_BASE_VERSION_RELEASE == component->mca_release_version) {
392         ioc_200 = (mca_io_base_component_2_0_0_t *) component;
393 
394         return query_2_0_0(ioc_200, file);
395     }
396 
397     /* Unknown io API version -- return error */
398 
399     return NULL;
400 }
401 
402 
query_2_0_0(const mca_io_base_component_2_0_0_t * component,ompi_file_t * file)403 static avail_io_t *query_2_0_0(const mca_io_base_component_2_0_0_t *component,
404                                ompi_file_t *file)
405 {
406     int priority;
407     avail_io_t *avail;
408     const mca_io_base_module_2_0_0_t *module;
409     struct mca_io_base_file_t *module_data;
410 
411     /* Query v2.0.0 */
412 
413     avail = NULL;
414     module_data = NULL;
415     module = component->io_file_query(file, &module_data, &priority);
416     if (NULL != module) {
417         avail = OBJ_NEW(avail_io_t);
418         avail->ai_version = MCA_IO_BASE_V_2_0_0;
419         avail->ai_priority = priority;
420         avail->ai_component.v2_0_0 = *component;
421         avail->ai_module.v2_0_0 = *module;
422         avail->ai_module_data = module_data;
423     }
424 
425     return avail;
426 }
427 
428 
429 /**************************************************************************
430  * Unquery functions
431  **************************************************************************/
432 
unquery(avail_io_t * avail,ompi_file_t * file)433 static void unquery(avail_io_t *avail, ompi_file_t *file)
434 {
435     const mca_io_base_component_2_0_0_t *ioc_200;
436 
437     switch(avail->ai_version) {
438     case MCA_IO_BASE_V_2_0_0:
439         ioc_200 = &(avail->ai_component.v2_0_0);
440         ioc_200->io_file_unquery(file, avail->ai_module_data);
441         break;
442 
443     default:
444         break;
445     }
446 }
447 
448 
449 /**************************************************************************
450  * Module_Init functions
451  **************************************************************************/
452 
453 /*
454  * Initialize a module
455  */
module_init(ompi_file_t * file)456 static int module_init(ompi_file_t *file)
457 {
458     const mca_io_base_module_2_0_0_t *iom_200;
459 
460     switch(file->f_io_version) {
461     case MCA_IO_BASE_V_2_0_0:
462         iom_200 = &(file->f_io_selected_module.v2_0_0);
463         return iom_200->io_module_file_open(file->f_comm, file->f_filename,
464                                             file->f_amode, file->super.s_info,
465                                             file);
466         break;
467 
468     default:
469         return OMPI_ERROR;
470         break;
471     }
472 
473     /* No way to reach here */
474 }
475