root/branches/cu-security-branch/src/io/bmi/bmi.c @ 8330

Revision 8330, 57.0 KB (checked in by nlmills, 3 years ago)

revert cu-security-branch to before the attempted merge with Orange-Branch

Line 
1/*
2 * (C) 2001 Clemson University and The University of Chicago
3 *
4 * See COPYING in top-level directory.
5 */
6
7/** \file
8 *  \ingroup bmiint
9 *
10 *  Top-level BMI network interface routines.
11 */
12
13#include <errno.h>
14#include <string.h>
15#include <assert.h>
16#include <time.h>
17#include <sys/time.h>
18#include <stdio.h>
19
20#include "bmi.h"
21#include "bmi-method-support.h"
22#include "bmi-method-callback.h"
23#include "gossip.h"
24#include "reference-list.h"
25#include "op-list.h"
26#include "gen-locks.h"
27#include "str-utils.h"
28#include "id-generator.h"
29#include "pvfs2-internal.h"
30#include "pvfs2-debug.h"
31
32static int bmi_initialized_count = 0;
33static gen_mutex_t bmi_initialize_mutex = GEN_MUTEX_INITIALIZER;
34
35/*
36 * List of BMI addrs currently managed.
37 */
38static ref_list_p cur_ref_list = NULL;
39
40/* array to keep up with active contexts */
41static int context_array[BMI_MAX_CONTEXTS] = { 0 };
42static gen_mutex_t context_mutex = GEN_MUTEX_INITIALIZER;
43static gen_mutex_t ref_mutex = GEN_MUTEX_INITIALIZER;
44
45static QLIST_HEAD(forget_list);
46static gen_mutex_t forget_list_mutex = GEN_MUTEX_INITIALIZER;
47
48struct forget_item
49{
50    struct qlist_head link;
51    BMI_addr_t addr;
52};
53
54/*
55 * Static list of defined BMI methods.  These are pre-compiled into
56 * the client libraries and into the server.
57 */
58#ifdef __STATIC_METHOD_BMI_TCP__
59extern struct bmi_method_ops bmi_tcp_ops;
60#endif
61#ifdef __STATIC_METHOD_BMI_GM__
62extern struct bmi_method_ops bmi_gm_ops;
63#endif
64#ifdef __STATIC_METHOD_BMI_MX__
65extern struct bmi_method_ops bmi_mx_ops;
66#endif
67#ifdef __STATIC_METHOD_BMI_IB__
68extern struct bmi_method_ops bmi_ib_ops;
69#endif
70#ifdef __STATIC_METHOD_BMI_PORTALS__
71extern struct bmi_method_ops bmi_portals_ops;
72#endif
73
74static struct bmi_method_ops *const static_methods[] = {
75#ifdef __STATIC_METHOD_BMI_TCP__
76    &bmi_tcp_ops,
77#endif
78#ifdef __STATIC_METHOD_BMI_GM__
79    &bmi_gm_ops,
80#endif
81#ifdef __STATIC_METHOD_BMI_MX__
82    &bmi_mx_ops,
83#endif
84#ifdef __STATIC_METHOD_BMI_IB__
85    &bmi_ib_ops,
86#endif
87#ifdef __STATIC_METHOD_BMI_PORTALS__
88    &bmi_portals_ops,
89#endif
90    NULL
91};
92
93/*
94 * List of "known" BMI methods.  This is dynamic, starting with
95 * just the static ones above, and perhaps adding more if we turn
96 * back on dynamic module loading.
97 */
98static int known_method_count = 0;
99static struct bmi_method_ops **known_method_table = 0;
100
101/*
102 * List of active BMI methods.  These are the ones that will be
103 * dealt with for a test call, for example.  On a client, known methods
104 * become active only when someone calls BMI_addr_lookup().  On
105 * a server, all possibly active methods are known at startup time
106 * because we listen on them for the duration.
107 */
108static int active_method_count = 0;
109static gen_mutex_t active_method_count_mutex = GEN_MUTEX_INITIALIZER;
110
111static struct bmi_method_ops **active_method_table = NULL;
112static struct {
113    int iters_polled;  /* how many iterations since this method was polled */
114    int iters_active;  /* how many iterations since this method had action */
115    int plan;
116} *method_usage = NULL;
117static const int usage_iters_starvation = 100000;
118static const int usage_iters_active = 10000;
119static int global_flags;
120
121static int activate_method(const char *name, const char *listen_addr,
122    int flags);
123static void bmi_addr_drop(ref_st_p tmp_ref);
124static void bmi_check_forget_list(void);
125
126/** Initializes the BMI layer.  Must be called before any other BMI
127 *  functions.
128 *
129 *  \param method_list a comma separated list of BMI methods to
130 *         use
131 *  \param listen_addr a comma separated list of addresses to listen on
132 *         for each method (if needed)
133 *  \param flags initialization flags
134 *
135 *  \return 0 on success, -errno on failure
136 */
137int BMI_initialize(const char *method_list,
138                   const char *listen_addr,
139                   int flags)
140{
141    int ret = -1;
142    int i = 0, j = 0;
143    char **requested_methods = NULL;
144    char **listen_addrs = NULL;
145    char *this_addr = NULL;
146    char *proto = NULL;
147    int addr_count = 0;
148
149    gen_mutex_lock(&bmi_initialize_mutex);
150    if(bmi_initialized_count > 0)
151    {
152        /* Already initialized! Just increment ref count and return. */
153        ++bmi_initialized_count;
154        gen_mutex_unlock(&bmi_initialize_mutex);
155        return 0;
156    }
157    ++bmi_initialized_count;
158    gen_mutex_unlock(&bmi_initialize_mutex);
159
160    global_flags = flags;
161
162    /* server must specify method list at startup, optional for client */
163    if (flags & BMI_INIT_SERVER) {
164        if (!listen_addr || !method_list)
165            return bmi_errno_to_pvfs(-EINVAL);
166    } else {
167        if (listen_addr)
168            return bmi_errno_to_pvfs(-EINVAL);
169        if (flags) {
170            gossip_lerr("Warning: flags ignored on client.\n");
171        }
172    }
173
174    /* make sure that id generator is initialized if not already */
175    ret = id_gen_safe_initialize();
176    if(ret < 0)
177    {
178        return(ret);
179    }
180
181    /* make a new reference list */
182    cur_ref_list = ref_list_new();
183    if (!cur_ref_list)
184    {
185        ret = bmi_errno_to_pvfs(-ENOMEM);
186        goto bmi_initialize_failure;
187    }
188
189    /* initialize the known method list from the null-terminated static list */
190    known_method_count = sizeof(static_methods) / sizeof(static_methods[0]) - 1;
191    known_method_table = malloc(
192        known_method_count * sizeof(*known_method_table));
193    if (!known_method_table)
194        return bmi_errno_to_pvfs(-ENOMEM);
195    memcpy(known_method_table, static_methods,
196        known_method_count * sizeof(*known_method_table));
197
198    gen_mutex_lock(&active_method_count_mutex);
199    if (!method_list) {
200        /* nothing active until lookup */
201        active_method_count = 0;
202    } else {
203        /* split and initialize the requested method list */
204        int numreq = PINT_split_string_list(&requested_methods, method_list);
205        if (numreq < 1)
206        {
207            gossip_lerr("Error: bad method list.\n");
208            ret = bmi_errno_to_pvfs(-EINVAL);
209            gen_mutex_unlock(&active_method_count_mutex);
210            goto bmi_initialize_failure;
211        }
212
213        /* Today is that day! */
214        addr_count = PINT_split_string_list(&listen_addrs, listen_addr);
215       
216        for (i=0; i<numreq; i++) {
217
218            /* assume the method name is bmi_<proto>, and find the <proto>
219             * part
220             */
221            proto = strstr(requested_methods[i], "bmi_");
222            if(!proto)
223            {
224                gossip_err("%s: Invalid method name: %s.  Method names "
225                           "must start with 'bmi_'\n",
226                           __func__, requested_methods[i]);
227                ret = -EINVAL;
228                gen_mutex_unlock(&active_method_count_mutex);
229                goto bmi_initialize_failure;
230            }
231            proto += 4;
232
233            /* match the proper listen addr to the method */
234            for(j=0; j<addr_count; ++j)
235            {
236                /* we don't want a strstr here in case the addr has
237                 * the proto as part of the hostname
238                 */
239                if(!strncmp(listen_addrs[j], proto, strlen(proto)))
240                {
241                    /* found the right addr */
242                    this_addr = listen_addrs[j];
243                    break;
244                }
245            }
246               
247            if(!this_addr)
248            {
249                /* couldn't find the right listen addr */
250                gossip_err("%s: Failed to find an appropriate listening "
251                           "address for the bmi method: %s\n",
252                           __func__, requested_methods[i]);
253                ret = -EINVAL;
254                gen_mutex_unlock(&active_method_count_mutex);
255                goto bmi_initialize_failure;
256            }
257
258            ret = activate_method(requested_methods[i], this_addr, flags);
259            if (ret < 0) {
260                ret = bmi_errno_to_pvfs(ret);
261                gen_mutex_unlock(&active_method_count_mutex);
262                goto bmi_initialize_failure;
263            }
264            free(requested_methods[i]);
265        }
266        free(requested_methods);
267        if(listen_addrs)
268        {
269            PINT_free_string_list(listen_addrs, addr_count);
270            listen_addrs = NULL;
271        }
272    }
273    gen_mutex_unlock(&active_method_count_mutex);
274
275    return (0);
276
277  bmi_initialize_failure:
278
279    /* kill reference list */
280    if (cur_ref_list)
281    {
282        ref_list_cleanup(cur_ref_list);
283    }
284
285    gen_mutex_lock(&active_method_count_mutex);
286    /* look for loaded methods and shut down */
287    if (active_method_table)
288    {
289        for (i = 0; i < active_method_count; i++)
290        {
291            if (active_method_table[i])
292            {
293                active_method_table[i]->finalize();
294            }
295        }
296        free(active_method_table);
297    }
298
299    if (known_method_table) {
300        free(known_method_table);
301        known_method_count = 0;
302    }
303
304    /* get rid of method string list */
305    if (requested_methods)
306    {
307        for (i = 0; i < active_method_count; i++)
308        {
309            if (requested_methods[i])
310            {
311                free(requested_methods[i]);
312            }
313        }
314        free(requested_methods);
315    }
316
317    if(listen_addrs)
318    {
319        PINT_free_string_list(listen_addrs, addr_count);
320    }
321
322    active_method_count = 0;
323    gen_mutex_unlock(&active_method_count_mutex);
324
325    /* shut down id generator */
326    id_gen_safe_finalize();
327
328    return (ret);
329}
330
331/* the following is the old BMI_initialize() function that used dl to
332 * pull in method modules dynamically.  Just hanging around as an
333 * example...
334 */
335#if 0
336/* BMI_initialize()
337 *
338 * Initializes the BMI layer.  Must be called before any other BMI
339 * functions.  module_string is a comma separated list of BMI modules to
340 * use, listen_addr is a comma separated list of addresses to listen on
341 * for each module (if needed), and flags are initialization flags.
342 *
343 * returns 0 on success, -errno on failure
344 */
345int BMI_initialize(const char *module_string,
346                   const char *listen_addr,
347                   int flags)
348{
349
350    int ret = -1;
351    int i = 0;
352    char **modules = NULL;
353    void *meth_mod = NULL;
354    char *mod_error = NULL;
355    method_addr_p new_addr = NULL;
356    op_list_p olp = NULL;
357
358    /* TODO: this is a hack to make sure we get all of the symbols loaded
359     * into the library... is there a better way?
360     */
361    olp = op_list_new();
362    op_list_cleanup(olp);
363
364    if (((flags & BMI_INIT_SERVER) && (!listen_addr)) || !module_string)
365    {
366        return (bmi_errno_to_pvfs(-EINVAL));
367    }
368
369    /* separate out the module list */
370    active_method_count = PINT_split_string_list(
371        &modules, module_string);
372    if (active_method_count < 1)
373    {
374        gossip_lerr("Error: bad module list.\n");
375        ret = bmi_errno_to_pvfs(-EINVAL);
376        goto bmi_initialize_failure;
377    }
378
379    /* create a table to keep up with the method modules */
380    active_method_table = (struct bmi_method_ops **)malloc(
381        active_method_count * sizeof(struct bmi_method_ops *));
382    if (!active_method_table)
383    {
384        ret = bmi_errno_to_pvfs(-ENOMEM);
385        goto bmi_initialize_failure;
386    }
387
388    /* iterate through each method in the list and load its module */
389    for (i = 0; i < active_method_count; i++)
390    {
391        meth_mod = dlopen(modules[i], RTLD_NOW);
392        if (!meth_mod)
393        {
394            gossip_lerr("Error: could not open module: %s\n", dlerror());
395            ret = bmi_errno_to_pvfs(-EINVAL);
396            goto bmi_initialize_failure;
397        }
398        dlerror();
399
400        active_method_table[i] = (struct bmi_method_ops *)
401            dlsym(meth_mod, "method_interface");
402        mod_error = dlerror();
403        if (mod_error)
404        {
405            gossip_lerr("Error: module load: %s\n", mod_error);
406            ret = bmi_errno_to_pvfs(-EINVAL);
407            goto bmi_initialize_failure;
408        }
409    }
410
411    /* make a new reference list */
412    cur_ref_list = ref_list_new();
413    if (!cur_ref_list)
414    {
415        ret = bmi_errno_to_pvfs(-ENOMEM);
416        goto bmi_initialize_failure;
417    }
418
419    /* initialize methods */
420    for (i = 0; i < active_method_count; i++)
421    {
422        if (flags & BMI_INIT_SERVER)
423        {
424            if ((new_addr =
425                 active_method_table[i]->
426                 BMI_meth_method_addr_lookup(listen_addr)) != NULL)
427            {
428                /* this is a bit of a hack */
429                new_addr->method_type = i;
430                ret = active_method_table[i]->BMI_meth_initialize(
431                    new_addr, i, flags);
432            }
433            else
434            {
435                ret = -1;
436            }
437        }
438        else
439        {
440            ret = active_method_table[i]->BMI_meth_initialize(
441                NULL, i, flags);
442        }
443        if (ret < 0)
444        {
445            gossip_lerr("Error: initializing module: %s\n", modules[i]);
446            goto bmi_initialize_failure;
447        }
448    }
449
450    return (0);
451
452  bmi_initialize_failure:
453
454    /* kill reference list */
455    if (cur_ref_list)
456    {
457        ref_list_cleanup(cur_ref_list);
458    }
459
460    /* look for loaded methods and shut down */
461    if (active_method_table)
462    {
463        for (i = 0; i < active_method_count; i++)
464        {
465            if (active_method_table[i])
466            {
467                active_method_table[i]->BMI_meth_finalize();
468            }
469        }
470        free(active_method_table);
471    }
472
473    /* get rid of method string list */
474    if (modules)
475    {
476        for (i = 0; i < active_method_count; i++)
477        {
478            if (modules[i])
479            {
480                free(modules[i]);
481            }
482        }
483        free(modules);
484    }
485
486    return (ret);
487}
488#endif /* 0 */
489
490/** Shuts down the BMI layer.
491 *
492 * \return 0.
493 */
494int BMI_finalize(void)
495{
496    int i = -1;
497
498    gen_mutex_lock(&bmi_initialize_mutex);
499    --bmi_initialized_count;
500    if(bmi_initialized_count > 0)
501    {
502        gen_mutex_unlock(&bmi_initialize_mutex);
503        return 0;
504    }
505    gen_mutex_unlock(&bmi_initialize_mutex);
506
507    gen_mutex_lock(&active_method_count_mutex);
508    /* attempt to shut down active methods */
509    for (i = 0; i < active_method_count; i++)
510    {
511        active_method_table[i]->finalize();
512    }
513    active_method_count = 0;
514    free(active_method_table);
515    gen_mutex_unlock(&active_method_count_mutex);
516
517    free(known_method_table);
518    known_method_count = 0;
519
520    if (method_usage)
521        free(method_usage);
522
523    /* destroy the reference list */
524    /* (side effect: destroys all method addresses as well) */
525    ref_list_cleanup(cur_ref_list);
526
527    /* shut down id generator */
528    id_gen_safe_finalize();
529
530    return (0);
531}
532
533/** Creates a new context to be used for communication.  This can be
534 *  used, for example, to distinguish between operations posted by
535 *  different threads.
536 *
537 *  \return 0 on success, -errno on failure.
538 */
539int BMI_open_context(bmi_context_id* context_id)
540{
541    int context_index;
542    int i;
543    int ret = 0;
544
545    gen_mutex_lock(&context_mutex);
546
547    /* find an unused context id */
548    for(context_index=0; context_index<BMI_MAX_CONTEXTS; context_index++)
549    {
550        if(context_array[context_index] == 0)
551        {
552            break;
553        }
554    }
555
556    if(context_index >= BMI_MAX_CONTEXTS)
557    {
558        /* we don't have any more available! */
559        gen_mutex_unlock(&context_mutex);
560        return(bmi_errno_to_pvfs(-EBUSY));
561    }
562
563    gen_mutex_lock(&active_method_count_mutex);
564    /* tell all of the modules about the new context */
565    for (i = 0; i < active_method_count; i++)
566    {
567        ret = active_method_table[i]->open_context(
568            context_index);
569        if(ret < 0)
570        {
571            /*
572              one of them failed; kill this context in the previous
573              modules
574            */
575            --i;
576            while (i >= 0)
577            {
578                active_method_table[i]->close_context(
579                    context_index);
580                --i;
581            }
582            goto out;
583        }
584    }
585    gen_mutex_unlock(&active_method_count_mutex);
586
587    context_array[context_index] = 1;
588    *context_id = context_index;
589
590out:
591
592    gen_mutex_unlock(&context_mutex);
593    return(ret);
594}
595
596
597/** Destroys a context previous generated with BMI_open_context().
598 */
599void BMI_close_context(bmi_context_id context_id)
600{
601    int i;
602
603    gen_mutex_lock(&context_mutex);
604
605    if(!context_array[context_id])
606    {
607        gen_mutex_unlock(&context_mutex);
608        return;
609    }
610
611    /* tell all of the modules to get rid of this context */
612    gen_mutex_lock(&active_method_count_mutex);
613    for (i = 0; i < active_method_count; i++)
614    {
615        active_method_table[i]->close_context(context_id);
616    }
617    context_array[context_id] = 0;
618    gen_mutex_unlock(&active_method_count_mutex);
619
620    gen_mutex_unlock(&context_mutex);
621    return;
622}
623
624
625/** Submits receive operations for subsequent service.
626 *
627 *  \return 0 on success, -errno on failure.
628 */
629int BMI_post_recv(bmi_op_id_t * id,
630                  BMI_addr_t src,
631                  void *buffer,
632                  bmi_size_t expected_size,
633                  bmi_size_t * actual_size,
634                  enum bmi_buffer_type buffer_type,
635                  bmi_msg_tag_t tag,
636                  void *user_ptr,
637                  bmi_context_id context_id,
638                  bmi_hint hints)
639{
640    ref_st_p tmp_ref = NULL;
641    int ret = -1;
642
643    gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
644                 "BMI_post_recv: addr: %ld, offset: 0x%lx, size: %ld, tag: %d\n",
645                 (long)src, (long)buffer, (long)expected_size, (int)tag);
646
647    *id = 0;
648
649    gen_mutex_lock(&ref_mutex);
650    tmp_ref = ref_list_search_addr(cur_ref_list, src);
651    if (!tmp_ref)
652    {
653        gen_mutex_unlock(&ref_mutex);
654        return (bmi_errno_to_pvfs(-EPROTO));
655    }
656    gen_mutex_unlock(&ref_mutex);
657
658    ret = tmp_ref->interface->post_recv(
659        id, tmp_ref->method_addr, buffer, expected_size, actual_size,
660        buffer_type, tag, user_ptr, context_id, (PVFS_hint)hints);
661    return (ret);
662}
663
664
665/** Submits send operations for subsequent service.
666 *
667 *  \return 0 on success, -errno on failure.
668 */
669int BMI_post_send(bmi_op_id_t * id,
670                  BMI_addr_t dest,
671                  const void *buffer,
672                  bmi_size_t size,
673                  enum bmi_buffer_type buffer_type,
674                  bmi_msg_tag_t tag,
675                  void *user_ptr,
676                  bmi_context_id context_id,
677                  bmi_hint hints)
678{
679    ref_st_p tmp_ref = NULL;
680    int ret = -1;
681
682    gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
683                 "BMI_post_send: addr: %ld, offset: 0x%lx, size: %ld, tag: %d\n",
684                 (long)dest, (long)buffer, (long)size, (int)tag);
685
686    *id = 0;
687
688    gen_mutex_lock(&ref_mutex);
689    tmp_ref = ref_list_search_addr(cur_ref_list, dest);
690    if (!tmp_ref)
691    {
692        gen_mutex_unlock(&ref_mutex);
693        return (bmi_errno_to_pvfs(-EPROTO));
694    }
695    gen_mutex_unlock(&ref_mutex);
696
697    ret = tmp_ref->interface->post_send(
698        id, tmp_ref->method_addr, buffer, size, buffer_type, tag,
699        user_ptr, context_id, (PVFS_hint)hints);
700    return (ret);
701}
702
703
704/** Submits unexpected send operations for subsequent service.
705 *
706 *  \return 0 on success, -errno on failure.
707 */
708int BMI_post_sendunexpected(bmi_op_id_t * id,
709                            BMI_addr_t dest,
710                            const void *buffer,
711                            bmi_size_t size,
712                            enum bmi_buffer_type buffer_type,
713                            bmi_msg_tag_t tag,
714                            void *user_ptr,
715                            bmi_context_id context_id,
716                            bmi_hint hints)
717{
718    ref_st_p tmp_ref = NULL;
719    int ret = -1;
720
721    gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
722        "BMI_post_sendunexpected: addr: %ld, offset: 0x%lx, size: %ld, tag: %d\n",
723        (long)dest, (long)buffer, (long)size, (int)tag);
724
725    *id = 0;
726
727    gen_mutex_lock(&ref_mutex);
728    tmp_ref = ref_list_search_addr(cur_ref_list, dest);
729    if (!tmp_ref)
730    {
731        gen_mutex_unlock(&ref_mutex);
732        return (bmi_errno_to_pvfs(-EPROTO));
733    }
734    gen_mutex_unlock(&ref_mutex);
735
736    ret = tmp_ref->interface->post_sendunexpected(
737        id, tmp_ref->method_addr, buffer, size, buffer_type, tag,
738        user_ptr, context_id, (PVFS_hint)hints);
739    return (ret);
740}
741
742
743/** Checks to see if a particular message has completed.
744 *
745 *  \return 0 on success, -errno on failure.
746 */
747int BMI_test(bmi_op_id_t id,
748             int *outcount,
749             bmi_error_code_t * error_code,
750             bmi_size_t * actual_size,
751             void **user_ptr,
752             int max_idle_time_ms,
753             bmi_context_id context_id)
754{
755    struct method_op *target_op = NULL;
756    int ret = -1;
757
758    if (max_idle_time_ms < 0)
759        return (bmi_errno_to_pvfs(-EINVAL));
760
761    *outcount = 0;
762
763    target_op = id_gen_fast_lookup(id);
764    assert(target_op->op_id == id);
765
766    ret = active_method_table[
767        target_op->addr->method_type]->test(
768            id, outcount, error_code, actual_size, user_ptr,
769            max_idle_time_ms, context_id);
770
771    /* return 1 if anything completed */
772    if (ret == 0 && *outcount == 1)
773    {
774        gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
775                     "BMI_test completing: %llu\n", llu(id));
776        return (1);
777    }
778    return (ret);
779}
780
781
782/** Checks to see if any messages from the specified list have completed.
783 *
784 * \return 0 on success, -errno on failure.
785 *
786 * XXX: never used.  May want to add adaptive polling strategy of testcontext
787 * if it becomes used again.
788 */
789int BMI_testsome(int incount,
790                 bmi_op_id_t * id_array,
791                 int *outcount,
792                 int *index_array,
793                 bmi_error_code_t * error_code_array,
794                 bmi_size_t * actual_size_array,
795                 void **user_ptr_array,
796                 int max_idle_time_ms,
797                 bmi_context_id context_id)
798{
799    int ret = 0;
800    int idle_per_method = 0;
801    bmi_op_id_t* tmp_id_array;
802    int i,j;
803    struct method_op *query_op;
804    int need_to_test;
805    int tmp_outcount;
806    int tmp_active_method_count;
807
808    gen_mutex_lock(&active_method_count_mutex);
809    tmp_active_method_count = active_method_count;
810    gen_mutex_unlock(&active_method_count_mutex);
811
812    if (max_idle_time_ms < 0)
813        return (bmi_errno_to_pvfs(-EINVAL));
814
815    *outcount = 0;
816
817    if (tmp_active_method_count == 1) {
818        /* shortcircuit for perhaps common case of only one method */
819        ret = active_method_table[0]->testsome(
820            incount, id_array, outcount, index_array,
821            error_code_array, actual_size_array, user_ptr_array,
822            max_idle_time_ms, context_id);
823
824        /* return 1 if anything completed */
825        if (ret == 0 && *outcount > 0)
826            return (1);
827        else
828            return ret;
829    }
830
831    /* TODO: do something more clever here */
832    if (max_idle_time_ms)
833    {
834        idle_per_method = max_idle_time_ms / tmp_active_method_count;
835        if (!idle_per_method)
836            idle_per_method = 1;
837    }
838
839    tmp_id_array = (bmi_op_id_t*)malloc(incount*sizeof(bmi_op_id_t));
840    if(!tmp_id_array)
841        return(bmi_errno_to_pvfs(-ENOMEM));
842
843    /* iterate over each active method */
844    for(i=0; i<tmp_active_method_count; i++)
845    {
846        /* setup the tmp id array with only operations that match
847         * that method
848         */
849        need_to_test = 0;
850        for(j=0; j<incount; j++)
851        {
852            if(id_array[j])
853            {
854                query_op = (struct method_op*)
855                    id_gen_fast_lookup(id_array[j]);
856                assert(query_op->op_id == id_array[j]);
857                if(query_op->addr->method_type == i)
858                {
859                    tmp_id_array[j] = id_array[j];
860                    need_to_test++;
861                }
862            }
863        }
864
865        /* call testsome if we found any ops for this method */
866        if(need_to_test)
867        {
868            tmp_outcount = 0;
869            ret = active_method_table[i]->testsome(
870                need_to_test, tmp_id_array, &tmp_outcount,
871                &(index_array[*outcount]),
872                &(error_code_array[*outcount]),
873                &(actual_size_array[*outcount]),
874                user_ptr_array ? &(user_ptr_array[*outcount]) : 0,
875                idle_per_method,
876                context_id);
877            if(ret < 0)
878            {
879                /* can't recover from this... */
880                gossip_lerr("Error: critical BMI_testsome failure.\n");
881                goto out;
882            }
883            *outcount += tmp_outcount;
884        }
885    }
886
887  out:
888    free(tmp_id_array);
889
890    if(ret == 0 && *outcount > 0)
891        return(1);
892    else
893        return(0);
894}
895
896
897/*
898 * If some method was recently active, poll it again for speed,
899 * but be sure not to starve any method.  If multiple active,
900 * poll them all.  Return idle_time per method too.
901 */
902static void
903construct_poll_plan(int nmeth, int *idle_time_ms)
904{
905    int i, numplan;
906
907    numplan = 0;
908    for (i=0; i<nmeth; i++) {
909        ++method_usage[i].iters_polled;
910        ++method_usage[i].iters_active;
911        method_usage[i].plan = 0;
912        if (method_usage[i].iters_active <= usage_iters_active) {
913            /* recently busy, poll */
914            if (0) gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
915                         "%s: polling active meth %d: %d / %d\n", __func__, i,
916                         method_usage[i].iters_active, usage_iters_active);
917            method_usage[i].plan = 1;
918            ++numplan;
919            *idle_time_ms = 0;  /* busy polling */
920        } else if (method_usage[i].iters_polled >= usage_iters_starvation) {
921            /* starving, time to poke this one */
922            if (0) gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
923                         "%s: polling starving meth %d: %d / %d\n", __func__, i,
924                         method_usage[i].iters_polled, usage_iters_starvation);
925            method_usage[i].plan = 1;
926            ++numplan;
927        }
928    }
929
930    /* if nothing is starving or busy, poll everybody */
931    if (numplan == 0) {
932        for (i=0; i<nmeth; i++)
933            method_usage[i].plan = 1;
934        numplan = nmeth;
935
936        /* spread idle time evenly */
937        if (*idle_time_ms) {
938            *idle_time_ms /= numplan;
939            if (*idle_time_ms == 0)
940                *idle_time_ms = 1;
941        }
942        /* note that BMI_testunexpected is always called with idle_time 0 */
943        if (0) gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
944                     "%s: polling all %d methods, idle %d ms\n", __func__,
945                     numplan, *idle_time_ms);
946    }
947}
948
949
950/** Checks to see if any unexpected messages have completed.
951 *
952 *  \return 0 on success, -errno on failure.
953 */
954int BMI_testunexpected(int incount,
955                       int *outcount,
956                       struct BMI_unexpected_info *info_array,
957                       int max_idle_time_ms)
958{
959    int i = 0;
960    int ret = -1;
961    int position = 0;
962    int tmp_outcount = 0;
963    struct bmi_method_unexpected_info sub_info[incount];
964    ref_st_p tmp_ref = NULL;
965    int tmp_active_method_count = 0;
966
967    /* figure out if we need to drop any stale addresses */
968    bmi_check_forget_list();
969
970    gen_mutex_lock(&active_method_count_mutex);
971    tmp_active_method_count = active_method_count;
972    gen_mutex_unlock(&active_method_count_mutex);
973
974    if (max_idle_time_ms < 0)
975        return (bmi_errno_to_pvfs(-EINVAL));
976
977    *outcount = 0;
978
979    construct_poll_plan(tmp_active_method_count, &max_idle_time_ms);
980
981    while (position < incount && i < tmp_active_method_count)
982    {
983        if (method_usage[i].plan) {
984            ret = active_method_table[i]->testunexpected(
985                (incount - position), &tmp_outcount,
986                (&(sub_info[position])), max_idle_time_ms);
987            if (ret < 0)
988            {
989                /* can't recover from this */
990                gossip_lerr("Error: critical BMI_testunexpected failure.\n");
991                return (ret);
992            }
993            position += tmp_outcount;
994            (*outcount) += tmp_outcount;
995            method_usage[i].iters_polled = 0;
996            if (ret)
997                method_usage[i].iters_active = 0;
998        }
999        i++;
1000    }
1001
1002    for (i = 0; i < (*outcount); i++)
1003    {
1004        info_array[i].error_code = sub_info[i].error_code;
1005        info_array[i].buffer = sub_info[i].buffer;
1006        info_array[i].size = sub_info[i].size;
1007        info_array[i].tag = sub_info[i].tag;
1008        gen_mutex_lock(&ref_mutex);
1009        tmp_ref = ref_list_search_method_addr(
1010            cur_ref_list, sub_info[i].addr);
1011        if (!tmp_ref)
1012        {
1013            /* yeah, right */
1014            gossip_lerr("Error: critical BMI_testunexpected failure.\n");
1015            gen_mutex_unlock(&ref_mutex);
1016            return (bmi_errno_to_pvfs(-EPROTO));
1017        }
1018        if(global_flags & BMI_AUTO_REF_COUNT)
1019        {
1020            tmp_ref->ref_count++;
1021        }
1022        gen_mutex_unlock(&ref_mutex);
1023        info_array[i].addr = tmp_ref->bmi_addr;
1024    }
1025    /* return 1 if anything completed */
1026    if (ret == 0 && *outcount > 0)
1027    {
1028        return (1);
1029    }
1030    return (0);
1031}
1032
1033
1034/** Checks to see if any messages from the specified context have
1035 *  completed.
1036 *
1037 *  \returns 0 on success, -errno on failure.
1038 */
1039int BMI_testcontext(int incount,
1040                    bmi_op_id_t* out_id_array,
1041                    int *outcount,
1042                    bmi_error_code_t * error_code_array,
1043                    bmi_size_t * actual_size_array,
1044                    void **user_ptr_array,
1045                    int max_idle_time_ms,
1046                    bmi_context_id context_id)
1047{
1048    int i = 0;
1049    int ret = -1;
1050    int position = 0;
1051    int tmp_outcount = 0;
1052    int tmp_active_method_count = 0;
1053    struct timespec ts;
1054
1055    gen_mutex_lock(&active_method_count_mutex);
1056    tmp_active_method_count = active_method_count;
1057    gen_mutex_unlock(&active_method_count_mutex);
1058
1059    if (max_idle_time_ms < 0)
1060        return (bmi_errno_to_pvfs(-EINVAL));
1061
1062    *outcount = 0;
1063
1064    if(tmp_active_method_count < 1)
1065    {
1066        /* nothing active yet, just snooze and return */
1067        if(max_idle_time_ms > 0)
1068        {
1069            ts.tv_sec = 0;
1070            ts.tv_nsec = 2000;
1071            nanosleep(&ts, NULL);
1072        }
1073        return(0);
1074    }
1075
1076    construct_poll_plan(tmp_active_method_count, &max_idle_time_ms);
1077
1078    while (position < incount && i < tmp_active_method_count)
1079    {
1080        if (method_usage[i].plan) {
1081            ret = active_method_table[i]->testcontext(
1082                incount - position,
1083                &out_id_array[position],
1084                &tmp_outcount,
1085                &error_code_array[position],
1086                &actual_size_array[position],
1087                user_ptr_array ?  &user_ptr_array[position] : NULL,
1088                max_idle_time_ms,
1089                context_id);
1090            if (ret < 0)
1091            {
1092                /* can't recover from this */
1093                gossip_lerr("Error: critical BMI_testcontext failure.\n");
1094                return (ret);
1095            }
1096            position += tmp_outcount;
1097            (*outcount) += tmp_outcount;
1098            method_usage[i].iters_polled = 0;
1099            if (ret)
1100                method_usage[i].iters_active = 0;
1101        }
1102        i++;
1103    }
1104
1105    /* return 1 if anything completed */
1106    if (ret == 0 && *outcount > 0)
1107    {
1108        for(i=0; i<*outcount; i++)
1109        {
1110            gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
1111                "BMI_testcontext completing: %llu\n", llu(out_id_array[i]));
1112        }
1113        return (1);
1114    }
1115    return (0);
1116
1117}
1118
1119
1120/** Performs a reverse lookup, returning the string (URL style)
1121 *  address for a given opaque address.
1122 *
1123 *  NOTE: caller must not free or modify returned string
1124 *
1125 *  \return Pointer to string on success, NULL on failure.
1126 */
1127const char* BMI_addr_rev_lookup(BMI_addr_t addr)
1128{
1129    ref_st_p tmp_ref = NULL;
1130    char* tmp_str = NULL;
1131
1132    /* find a reference that matches this address */
1133    gen_mutex_lock(&ref_mutex);
1134    tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1135    if (!tmp_ref)
1136    {
1137        gen_mutex_unlock(&ref_mutex);
1138        return (NULL);
1139    }
1140    gen_mutex_unlock(&ref_mutex);
1141   
1142    tmp_str = tmp_ref->id_string;
1143
1144    return(tmp_str);
1145}
1146
1147/** Performs a reverse lookup, returning a string
1148 *  address for a given opaque address.  Works on any address, even those
1149 *  generated unexpectedly, but only gives hostname instead of full
1150 *  BMI URL style address
1151 *
1152 *  NOTE: caller must not free or modify returned string
1153 *
1154 *  \return Pointer to string on success, NULL on failure.
1155 */
1156const char* BMI_addr_rev_lookup_unexpected(BMI_addr_t addr)
1157{
1158    ref_st_p tmp_ref = NULL;
1159
1160    /* find a reference that matches this address */
1161    gen_mutex_lock(&ref_mutex);
1162    tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1163    if (!tmp_ref)
1164    {
1165        gen_mutex_unlock(&ref_mutex);
1166        return ("UNKNOWN");
1167    }
1168    gen_mutex_unlock(&ref_mutex);
1169   
1170    if(!tmp_ref->interface->rev_lookup_unexpected)
1171    {
1172        return("UNKNOWN");
1173    }
1174
1175    return(tmp_ref->interface->rev_lookup_unexpected(
1176        tmp_ref->method_addr));
1177}
1178
1179
1180/** Allocates memory that can be used in native mode by the BMI layer.
1181 *
1182 *  \return Pointer to buffer on success, NULL on failure.
1183 */
1184void *BMI_memalloc(BMI_addr_t addr,
1185                   bmi_size_t size,
1186                   enum bmi_op_type send_recv)
1187{
1188    void *new_buffer = NULL;
1189    ref_st_p tmp_ref = NULL;
1190
1191    /* find a reference that matches this address */
1192    gen_mutex_lock(&ref_mutex);
1193    tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1194    if (!tmp_ref)
1195    {
1196        gen_mutex_unlock(&ref_mutex);
1197        return (NULL);
1198    }
1199    gen_mutex_unlock(&ref_mutex);
1200
1201    /* allocate the buffer using the method's mechanism */
1202    new_buffer = tmp_ref->interface->memalloc(size, send_recv);
1203
1204    return (new_buffer);
1205}
1206
1207/** Frees memory that was allocated with BMI_memalloc().
1208 *
1209 *  \return 0 on success, -errno on failure.
1210 */
1211int BMI_memfree(BMI_addr_t addr,
1212                void *buffer,
1213                bmi_size_t size,
1214                enum bmi_op_type send_recv)
1215{
1216    ref_st_p tmp_ref = NULL;
1217    int ret = -1;
1218
1219    /* find a reference that matches this address */
1220    gen_mutex_lock(&ref_mutex);
1221    tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1222    if (!tmp_ref)
1223    {
1224        gen_mutex_unlock(&ref_mutex);
1225        return (bmi_errno_to_pvfs(-EINVAL));
1226    }
1227    gen_mutex_unlock(&ref_mutex);
1228
1229    /* free the memory */
1230    ret = tmp_ref->interface->memfree(buffer, size, send_recv);
1231
1232    return (ret);
1233}
1234
1235/** Acknowledge that an unexpected message has been
1236 * serviced that was returned from BMI_test_unexpected().
1237 *
1238 *  \return 0 on success, -errno on failure.
1239 */
1240int BMI_unexpected_free(BMI_addr_t addr,
1241                void *buffer)
1242{
1243    ref_st_p tmp_ref = NULL;
1244    int ret = -1;
1245
1246    /* find a reference that matches this address */
1247    gen_mutex_lock(&ref_mutex);
1248    tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1249    if (!tmp_ref)
1250    {
1251        gen_mutex_unlock(&ref_mutex);
1252        return (bmi_errno_to_pvfs(-EINVAL));
1253    }
1254    gen_mutex_unlock(&ref_mutex);
1255
1256    if (!tmp_ref->interface->unexpected_free)
1257    {
1258        gossip_err("unimplemented unexpected_free callback\n");
1259        return bmi_errno_to_pvfs(-EOPNOTSUPP);
1260    }
1261    /* free the memory */
1262    ret = tmp_ref->interface->unexpected_free(buffer);
1263
1264    return (ret);
1265}
1266
1267/** Pass in optional parameters.
1268 *
1269 *  \return 0 on success, -errno on failure.
1270 */
1271int BMI_set_info(BMI_addr_t addr,
1272                 int option,
1273                 void *inout_parameter)
1274{
1275    int ret = -1;
1276    int i = 0;
1277    ref_st_p tmp_ref = NULL;
1278
1279    gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
1280                 "[BMI CONTROL]: %s: set_info: %llu option: %d\n",
1281                 __func__, llu(addr), option);
1282    /* if the addr is NULL, then the set_info should apply to all
1283     * available methods.
1284     */
1285    if (!addr)
1286    {
1287        if (!active_method_table)
1288        {
1289            return (bmi_errno_to_pvfs(-EINVAL));
1290        }
1291        gen_mutex_lock(&active_method_count_mutex);
1292        for (i = 0; i < active_method_count; i++)
1293        {
1294            ret = active_method_table[i]->set_info(
1295                option, inout_parameter);
1296            /* we bail out if even a single set_info fails */
1297            if (ret < 0)
1298            {
1299                gossip_lerr(
1300                    "Error: failure on set_info to method: %d\n", i);
1301                gen_mutex_unlock(&active_method_count_mutex);
1302                return (ret);
1303            }
1304        }
1305        gen_mutex_unlock(&active_method_count_mutex);
1306        return (0);
1307    }
1308
1309    gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
1310                 "[BMI CONTROL]: %s: searching for ref %llu\n",
1311                 __func__, llu(addr));
1312    /* find a reference that matches this address */
1313    gen_mutex_lock(&ref_mutex);
1314    tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1315    if (!tmp_ref)
1316    {
1317        gen_mutex_unlock(&ref_mutex);
1318        return (bmi_errno_to_pvfs(-EINVAL));
1319    }
1320
1321    /* shortcut address reference counting */
1322    if(option == BMI_INC_ADDR_REF)
1323    {
1324        tmp_ref->ref_count++;
1325        gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
1326                     "[BMI CONTROL]: %s: incremented ref %llu to: %d\n",
1327                     __func__, llu(addr), tmp_ref->ref_count);
1328        gen_mutex_unlock(&ref_mutex);
1329        return(0);
1330    }
1331    if(option == BMI_DEC_ADDR_REF)
1332    {
1333        tmp_ref->ref_count--;
1334        gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
1335                     "[BMI CONTROL]: %s: decremented ref %llu to: %d\n",
1336                     __func__, llu(addr), tmp_ref->ref_count);
1337        assert(tmp_ref->ref_count >= 0);
1338
1339        if(tmp_ref->ref_count == 0)
1340        {
1341            bmi_addr_drop(tmp_ref);
1342        }
1343        gen_mutex_unlock(&ref_mutex);
1344        return(0);
1345    }
1346
1347    /* if the caller requests a TCP specific close socket action */
1348    if (option == BMI_TCP_CLOSE_SOCKET)
1349    {
1350        /* check to see if the address is in fact a tcp address */
1351        if(strcmp(tmp_ref->interface->method_name, "bmi_tcp") == 0)
1352        {
1353            /* take the same action as in the BMI_DEC_ADDR_REF case to clean
1354             * out the entire address structure and anything linked to it so
1355             * that the next addr_lookup starts from scratch
1356             */
1357            gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
1358                         "[BMI CONTROL]: %s: Closing bmi_tcp "
1359                         "connection at caller's request.\n",
1360                         __func__);
1361            ref_list_rem(cur_ref_list, addr);
1362            dealloc_ref_st(tmp_ref);
1363        }
1364        gen_mutex_unlock(&ref_mutex);
1365        return 0;
1366    }
1367
1368    gen_mutex_unlock(&ref_mutex);
1369
1370    ret = tmp_ref->interface->set_info(option, inout_parameter);
1371
1372    return (ret);
1373}
1374
1375/** Query for optional parameters.
1376 *
1377 *  \return 0 on success, -errno on failure.
1378 */
1379int BMI_get_info(BMI_addr_t addr,
1380                 int option,
1381                 void *inout_parameter)
1382{
1383    int i = 0;
1384    int maxsize = 0;
1385    int tmp_maxsize;
1386    int ret = 0;
1387    ref_st_p tmp_ref = NULL;
1388
1389    switch (option)
1390    {
1391        /* check to see if the interface is initialized */
1392    case BMI_CHECK_INIT:
1393        gen_mutex_lock(&active_method_count_mutex);
1394        if (active_method_count > 0)
1395        {
1396            gen_mutex_unlock(&active_method_count_mutex);
1397            return (0);
1398        }
1399        else
1400        {
1401            gen_mutex_unlock(&active_method_count_mutex);
1402            return (bmi_errno_to_pvfs(-ENETDOWN));
1403        }
1404    case BMI_CHECK_MAXSIZE:
1405        gen_mutex_lock(&active_method_count_mutex);
1406        for (i = 0; i < active_method_count; i++)
1407        {
1408            ret = active_method_table[i]->get_info(
1409                option, &tmp_maxsize);
1410            if (ret < 0)
1411            {
1412                return (ret);
1413            }
1414            if (i == 0)
1415            {
1416                maxsize = tmp_maxsize;
1417            }
1418            else
1419            {
1420                if (tmp_maxsize < maxsize)
1421                    maxsize = tmp_maxsize;
1422            }
1423            *((int *) inout_parameter) = maxsize;
1424        }
1425        gen_mutex_unlock(&active_method_count_mutex);
1426        break;
1427    case BMI_GET_METH_ADDR:
1428        gen_mutex_lock(&ref_mutex);
1429        tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1430        if(!tmp_ref)
1431        {
1432            gen_mutex_unlock(&ref_mutex);
1433            return (bmi_errno_to_pvfs(-EINVAL));
1434        }
1435        gen_mutex_unlock(&ref_mutex);
1436        *((void**) inout_parameter) = tmp_ref->method_addr;
1437        break;
1438    case BMI_GET_UNEXP_SIZE:
1439        gen_mutex_lock(&ref_mutex);
1440        tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1441        if(!tmp_ref)
1442        {
1443            gen_mutex_unlock(&ref_mutex);
1444            return (bmi_errno_to_pvfs(-EINVAL));
1445        }
1446        gen_mutex_unlock(&ref_mutex);
1447        ret = tmp_ref->interface->get_info(
1448            option, inout_parameter);
1449        if(ret < 0)
1450        {
1451            return ret;
1452        }
1453        break;
1454
1455    default:
1456        return (bmi_errno_to_pvfs(-ENOSYS));
1457    }
1458    return (0);
1459}
1460
1461/** Given a string representation of a host/network address and a BMI
1462 * address handle, return whether the BMI address handle is part of the wildcard
1463 * address range specified by the string.
1464 * \return 1 on success, -errno on failure and 0 if it is not part of
1465 * the specified range
1466 */
1467int BMI_query_addr_range (BMI_addr_t addr, const char *id_string, int netmask)
1468{
1469    int ret = -1;
1470    int i = 0, failed = 1;
1471    int provided_method_length = 0;
1472    char *ptr, *provided_method_name = NULL;
1473    ref_st_p tmp_ref = NULL;
1474
1475    if((strlen(id_string)+1) > BMI_MAX_ADDR_LEN)
1476    {
1477        return(bmi_errno_to_pvfs(-ENAMETOOLONG));
1478    }
1479    /* lookup the provided address */
1480    gen_mutex_lock(&ref_mutex);
1481    tmp_ref = ref_list_search_addr(cur_ref_list, addr);
1482    if (!tmp_ref)
1483    {
1484        gen_mutex_unlock(&ref_mutex);
1485        return (bmi_errno_to_pvfs(-EPROTO));
1486    }
1487    gen_mutex_unlock(&ref_mutex);
1488
1489    ptr = strchr(id_string, ':');
1490    if (ptr == NULL)
1491    {
1492        return (bmi_errno_to_pvfs(-EINVAL));
1493    }
1494    ret = -EPROTO;
1495    provided_method_length = (unsigned long) ptr - (unsigned long) id_string;
1496    provided_method_name = (char *) calloc(provided_method_length + 1, sizeof(char));
1497    if (provided_method_name == NULL)
1498    {
1499        return bmi_errno_to_pvfs(-ENOMEM);
1500    }
1501    strncpy(provided_method_name, id_string, provided_method_length);
1502
1503    /* Now we will run through each method looking for one that
1504     * matches the specified wildcard address.
1505     */
1506    i = 0;
1507    gen_mutex_lock(&active_method_count_mutex);
1508    while (i < active_method_count)
1509    {
1510        const char *active_method_name = active_method_table[i]->method_name + 4;
1511        /* provided name matches this interface */
1512        if (!strncmp(active_method_name, provided_method_name, provided_method_length))
1513        {
1514            int (*meth_fnptr)(bmi_method_addr_p, const char *, int);
1515            failed = 0;
1516            if ((meth_fnptr = active_method_table[i]->query_addr_range) == NULL)
1517            {
1518                ret = -ENOSYS;
1519                gossip_lerr("Error: method doesn't implement querying address range/wildcards! Cannot implement FS export options!\n");
1520                failed = 1;
1521                break;
1522            }
1523            /* pass it into the specific bmi layer */
1524            ret = meth_fnptr(tmp_ref->method_addr, id_string, netmask);
1525            if (ret < 0)
1526                failed = 1;
1527            break;
1528        }
1529        i++;
1530    }
1531    gen_mutex_unlock(&active_method_count_mutex);
1532    free(provided_method_name);
1533    if (failed)
1534        return bmi_errno_to_pvfs(ret);
1535    return ret;
1536}
1537
1538/** Resolves the string representation of a host address into a BMI
1539 *  address handle.
1540 *
1541 *  \return 0 on success, -errno on failure.
1542 */
1543int BMI_addr_lookup(BMI_addr_t * new_addr,
1544                    const char *id_string)
1545{
1546
1547    ref_st_p new_ref = NULL;
1548    bmi_method_addr_p meth_addr = NULL;
1549    int ret = -1;
1550    int i = 0;
1551    int failed;
1552
1553    if((strlen(id_string)+1) > BMI_MAX_ADDR_LEN)
1554    {
1555        return(bmi_errno_to_pvfs(-ENAMETOOLONG));
1556    }
1557
1558    /* set the addr to zero in case we fail */
1559    *new_addr = 0;
1560
1561    /* First we want to check to see if this host has already been
1562     * discovered! */
1563    gen_mutex_lock(&ref_mutex);
1564    new_ref = ref_list_search_str(cur_ref_list, id_string);
1565    gen_mutex_unlock(&ref_mutex);
1566
1567    if (new_ref)
1568    {
1569        /* we found it. */
1570        *new_addr = new_ref->bmi_addr;
1571        return (0);
1572    }
1573
1574    /* Now we will run through each method looking for one that
1575     * responds successfully.  It is assumed that they are already
1576     * listed in order of preference
1577     */
1578    i = 0;
1579    gen_mutex_lock(&active_method_count_mutex);
1580    while ((i < active_method_count) &&
1581           !(meth_addr = active_method_table[i]->method_addr_lookup(id_string)))
1582    {
1583        i++;
1584    }
1585
1586    /* if not found, try to bring it up now */
1587    failed = 0;
1588    if (!meth_addr) {
1589        for (i=0; i<known_method_count; i++) {
1590            const char *name;
1591            /* only bother with those not active */
1592            int j;
1593            for (j=0; j<active_method_count; j++)
1594                if (known_method_table[i] == active_method_table[j])
1595                    break;
1596            if (j < active_method_count)
1597                continue;
1598
1599            /* well-known that mapping is "x" -> "bmi_x" */
1600            name = known_method_table[i]->method_name + 4;
1601            if (!strncmp(id_string, name, strlen(name))) {
1602                ret = activate_method(known_method_table[i]->method_name, 0, 0);
1603                if (ret < 0) {
1604                    failed = 1;
1605                    break;
1606                }
1607                meth_addr = known_method_table[i]->
1608                    method_addr_lookup(id_string);
1609                i = active_method_count - 1;  /* point at the new one */
1610                break;
1611            }
1612        }
1613    }
1614    gen_mutex_unlock(&active_method_count_mutex);
1615    if (failed)
1616        return bmi_errno_to_pvfs(ret);
1617
1618    /* make sure one was successful */
1619    if (!meth_addr)
1620    {
1621        return bmi_errno_to_pvfs(-ENOPROTOOPT);
1622    }
1623
1624    /* create a new reference for the addr */
1625    new_ref = alloc_ref_st();
1626    if (!new_ref)
1627    {
1628        ret = bmi_errno_to_pvfs(-ENOMEM);
1629        goto bmi_addr_lookup_failure;
1630    }
1631
1632    /* fill in the details */
1633    new_ref->method_addr = meth_addr;
1634    meth_addr->parent = new_ref;
1635    new_ref->id_string = (char *) malloc(strlen(id_string) + 1);
1636    if (!new_ref->id_string)
1637    {
1638        ret = bmi_errno_to_pvfs(errno);
1639        goto bmi_addr_lookup_failure;
1640    }
1641    strcpy(new_ref->id_string, id_string);
1642    new_ref->interface = active_method_table[i];
1643
1644    /* keep up with the reference and we are done */
1645    gen_mutex_lock(&ref_mutex);
1646    ref_list_add(cur_ref_list, new_ref);
1647    gen_mutex_unlock(&ref_mutex);
1648
1649    *new_addr = new_ref->bmi_addr;
1650    return (0);
1651
1652  bmi_addr_lookup_failure:
1653
1654    if (meth_addr)
1655    {
1656        active_method_table[i]->set_info(
1657            BMI_DROP_ADDR, meth_addr);
1658    }
1659
1660    if (new_ref)
1661    {
1662        dealloc_ref_st(new_ref);
1663    }
1664
1665    return (ret);
1666}
1667
1668
1669/** Similar to BMI_post_send(), except that the source buffer is
1670 *  replaced by a list of (possibly non contiguous) buffers.
1671 *
1672 *  \return 0 on success, 1 on immediate successful completion,
1673 *  -errno on failure.
1674 */
1675int BMI_post_send_list(bmi_op_id_t * id,
1676                       BMI_addr_t dest,
1677                       const void *const *buffer_list,
1678                       const bmi_size_t *size_list,
1679                       int list_count,
1680                       /* "total_size" is the sum of the size list */
1681                       bmi_size_t total_size,
1682                       enum bmi_buffer_type buffer_type,
1683                       bmi_msg_tag_t tag,
1684                       void *user_ptr,
1685                       bmi_context_id context_id,
1686                       bmi_hint hints)
1687{
1688    ref_st_p tmp_ref = NULL;
1689    int ret = -1;
1690
1691#ifndef GOSSIP_DISABLE_DEBUG
1692    int i;
1693
1694    gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
1695        "BMI_post_send_list: addr: %ld, count: %d, total_size: %ld, tag: %d\n",
1696        (long)dest, list_count, (long)total_size, (int)tag);
1697
1698    for(i=0; i<list_count; i++)
1699    {
1700        gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
1701            "   element %d: offset: 0x%lx, size: %ld\n",
1702            i, (long)buffer_list[i], (long)size_list[i]);
1703    }
1704#endif
1705
1706    *id = 0;
1707
1708    gen_mutex_lock(&ref_mutex);
1709    tmp_ref = ref_list_search_addr(cur_ref_list, dest);
1710    if (!tmp_ref)
1711    {
1712        gen_mutex_unlock(&ref_mutex);
1713        return (bmi_errno_to_pvfs(-EPROTO));
1714    }
1715    gen_mutex_unlock(&ref_mutex);
1716
1717    if (tmp_ref->interface->post_send_list)
1718    {
1719        ret = tmp_ref->interface->post_send_list(
1720            id, tmp_ref->method_addr, buffer_list, size_list,
1721            list_count, total_size, buffer_type, tag, user_ptr,
1722            context_id, (PVFS_hint)hints);
1723
1724        return (ret);
1725    }
1726
1727    gossip_lerr("Error: method doesn't implement send_list.\n");
1728    gossip_lerr("Error: send_list emulation not yet available.\n");
1729
1730    return (bmi_errno_to_pvfs(-ENOSYS));
1731}
1732
1733
1734/** Similar to BMI_post_recv(), except that the dest buffer is
1735 *  replaced by a list of (possibly non contiguous) buffers
1736 *
1737 *  \param total_expected_size the sum of the size list.
1738 *  \param total_actual_size the aggregate amt that was received.
1739 *
1740 *  \return 0 on success, 1 on immediate successful completion,
1741 *  -errno on failure.
1742 */
1743int BMI_post_recv_list(bmi_op_id_t * id,
1744                       BMI_addr_t src,
1745                       void *const *buffer_list,
1746                       const bmi_size_t *size_list,
1747                       int list_count,
1748                       bmi_size_t total_expected_size,
1749                       bmi_size_t * total_actual_size,
1750                       enum bmi_buffer_type buffer_type,
1751                       bmi_msg_tag_t tag,
1752                       void *user_ptr,
1753                       bmi_context_id context_id,
1754                       bmi_hint hints)
1755{
1756    ref_st_p tmp_ref = NULL;
1757    int ret = -1;
1758
1759#ifndef GOSSIP_DISABLE_DEBUG
1760    int i;
1761
1762    gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
1763        "BMI_post_recv_list: addr: %ld, count: %d, total_size: %ld, tag: %d\n",
1764        (long)src, list_count, (long)total_expected_size, (int)tag);
1765
1766    for(i=0; i<list_count; i++)
1767    {
1768        gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
1769            "   element %d: offset: 0x%lx, size: %ld\n",
1770            i, (long)buffer_list[i], (long)size_list[i]);
1771    }
1772#endif
1773
1774    *id = 0;
1775
1776    gen_mutex_lock(&ref_mutex);
1777    tmp_ref = ref_list_search_addr(cur_ref_list, src);
1778    if (!tmp_ref)
1779    {
1780        gen_mutex_unlock(&ref_mutex);
1781        return (bmi_errno_to_pvfs(-EPROTO));
1782    }
1783    gen_mutex_unlock(&ref_mutex);
1784
1785    if (tmp_ref->interface->post_recv_list)
1786    {
1787        ret = tmp_ref->interface->post_recv_list(
1788            id, tmp_ref->method_addr, buffer_list, size_list,
1789            list_count, total_expected_size, total_actual_size,
1790            buffer_type, tag, user_ptr, context_id, (PVFS_hint)hints);
1791
1792        return (ret);
1793    }
1794
1795    gossip_lerr("Error: method doesn't implement recv_list.\n");
1796    gossip_lerr("Error: recv_list emulation not yet available.\n");
1797
1798    return (bmi_errno_to_pvfs(-ENOSYS));
1799}
1800
1801
1802/** Similar to BMI_post_sendunexpected(), except that the source buffer is
1803 *  replaced by a list of (possibly non contiguous) buffers.
1804 *
1805 *  \param total_size the sum of the size list.
1806 *
1807 *  \return 0 on success, 1 on immediate successful completion,
1808 *  -errno on failure.
1809 */
1810int BMI_post_sendunexpected_list(bmi_op_id_t * id,
1811                                 BMI_addr_t dest,
1812                                 const void *const *buffer_list,
1813                                 const bmi_size_t *size_list,
1814                                 int list_count,
1815                                 bmi_size_t total_size,
1816                                 enum bmi_buffer_type buffer_type,
1817                                 bmi_msg_tag_t tag,
1818                                 void *user_ptr,
1819                                 bmi_context_id context_id,
1820                                 bmi_hint hints)
1821{
1822    ref_st_p tmp_ref = NULL;
1823    int ret = -1;
1824
1825#ifndef GOSSIP_DISABLE_DEBUG
1826    int i;
1827
1828    gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
1829        "BMI_post_sendunexpected_list: addr: %ld, count: %d, "
1830                 "total_size: %ld, tag: %d\n",  (long)dest, list_count,
1831                 (long)total_size, (int)tag);
1832
1833    for(i=0; i<list_count; i++)
1834    {
1835        gossip_debug(GOSSIP_BMI_DEBUG_OFFSETS,
1836            "   element %d: offset: 0x%lx, size: %ld\n",
1837            i, (long)buffer_list[i], (long)size_list[i]);
1838    }
1839#endif
1840
1841    *id = 0;
1842
1843    gen_mutex_lock(&ref_mutex);
1844    tmp_ref = ref_list_search_addr(cur_ref_list, dest);
1845    if (!tmp_ref)
1846    {
1847        gen_mutex_unlock(&ref_mutex);
1848        return (bmi_errno_to_pvfs(-EPROTO));
1849    }
1850    gen_mutex_unlock(&ref_mutex);
1851
1852    if (tmp_ref->interface->post_send_list)
1853    {
1854        ret = tmp_ref->interface->post_sendunexpected_list(
1855            id, tmp_ref->method_addr, buffer_list, size_list,
1856            list_count, total_size, buffer_type, tag, user_ptr,
1857            context_id, (PVFS_hint)hints);
1858
1859        return (ret);
1860    }
1861
1862    gossip_lerr("Error: method doesn't implement sendunexpected_list.\n");
1863    gossip_lerr("Error: send_list emulation not yet available.\n");
1864
1865    return (bmi_errno_to_pvfs(-ENOSYS));
1866}
1867
1868
1869/** Attempts to cancel a pending operation that has not yet completed.
1870 *  Caller must still test to gather error code after calling this
1871 *  function even if it returns 0.
1872 *
1873 *  \return 0 on success, -errno on failure.
1874 */
1875int BMI_cancel(bmi_op_id_t id,
1876               bmi_context_id context_id)
1877{
1878    struct method_op *target_op = NULL;
1879    int ret = -1;
1880
1881    gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
1882                 "%s: cancel id %llu\n", __func__, llu(id));
1883
1884    target_op = id_gen_fast_lookup(id);
1885    if(target_op == NULL)
1886    {
1887        /* if we can't find the operation, then assume it has already
1888         * completed naturally.
1889         */
1890        return(0);
1891    }
1892
1893    assert(target_op->op_id == id);
1894
1895    if(active_method_table[target_op->addr->method_type]->cancel)
1896    {
1897        ret = active_method_table[
1898            target_op->addr->method_type]->cancel(
1899                id, context_id);
1900    }
1901    else
1902    {
1903        gossip_err("Error: BMI_cancel() unimplemented "
1904                   "for this module.\n");
1905        ret = bmi_errno_to_pvfs(-ENOSYS);
1906    }
1907
1908    return (ret);
1909}
1910
1911/**************************************************************
1912 * method callback functions
1913 */
1914
1915/* bmi_method_addr_reg_callback()
1916 *
1917 * Used by the methods to register new addresses when they are
1918 * discovered.  Only call this method when the device gets an
1919 * unexpected receive from a new peer, i.e., if you do the equivalent
1920 * of a socket accept() and get a new connection.
1921 *
1922 * Do not call this function for active lookups, that is from your
1923 * method_addr_lookup.  BMI already knows about the address in
1924 * this case, since the user provided it.
1925 *
1926 * returns 0 on success, -errno on failure
1927 */
1928BMI_addr_t bmi_method_addr_reg_callback(bmi_method_addr_p map)
1929{
1930    ref_st_p new_ref = NULL;
1931
1932    /* NOTE: we are trusting the method to make sure that we really
1933     * don't know about the address yet.  No verification done here.
1934     */
1935
1936    /* create a new reference structure */
1937    new_ref = alloc_ref_st();
1938    if (!new_ref)
1939    {
1940        return 0;
1941    }
1942
1943    /*
1944      fill in the details; we don't have an id string for this one.
1945    */
1946    new_ref->method_addr = map;
1947    new_ref->id_string = NULL;
1948    map->parent = new_ref;
1949
1950    /* check the method_type from the method_addr pointer to know
1951     * which interface to use */
1952    new_ref->interface = active_method_table[map->method_type];
1953
1954    /* add the reference structure to the list */
1955    ref_list_add(cur_ref_list, new_ref);
1956
1957    return new_ref->bmi_addr;
1958}
1959
1960int bmi_method_addr_forget_callback(BMI_addr_t addr)
1961{
1962    struct forget_item* tmp_item = NULL;
1963
1964    tmp_item = (struct forget_item*)malloc(sizeof(struct forget_item));
1965    if(!tmp_item)
1966    {
1967        return(bmi_errno_to_pvfs(-ENOMEM));
1968    }
1969
1970    tmp_item->addr = addr;
1971
1972    /* add to queue of items that we want the BMI control layer to consider
1973     * deallocating
1974     */
1975    gen_mutex_lock(&forget_list_mutex);
1976    qlist_add(&tmp_item->link, &forget_list);
1977    gen_mutex_unlock(&forget_list_mutex);
1978
1979    return (0);
1980}
1981
1982/*
1983 * Attempt to insert this name into the list of active methods,
1984 * and bring it up.
1985 * NOTE: assumes caller has protected active_method_count with a mutex lock
1986 */
1987static int
1988activate_method(const char *name, const char *listen_addr, int flags)
1989{
1990    int i, ret;
1991    void *x;
1992    struct bmi_method_ops *meth;
1993    bmi_method_addr_p new_addr;
1994
1995    /* already active? */
1996    for (i=0; i<active_method_count; i++)
1997        if (!strcmp(active_method_table[i]->method_name, name)) break;
1998    if (i < active_method_count)
1999    {
2000        return 0;
2001    }
2002
2003    /* is the method known? */
2004    for (i=0; i<known_method_count; i++)
2005        if (!strcmp(known_method_table[i]->method_name, name)) break;
2006    if (i == known_method_count) {
2007        gossip_lerr("Error: no method available for %s.\n", name);
2008        return -ENOPROTOOPT;
2009    }
2010    meth = known_method_table[i];
2011
2012    /*
2013     * Later: try to load a dynamic module, growing the known method
2014     * table and search it again.
2015     */
2016
2017    /* toss it into the active table */
2018    x = active_method_table;
2019    active_method_table = malloc(
2020        (active_method_count + 1) * sizeof(*active_method_table));
2021    if (!active_method_table) {
2022        active_method_table = x;
2023        return -ENOMEM;
2024    }
2025    if (active_method_count) {
2026        memcpy(active_method_table, x,
2027            active_method_count * sizeof(*active_method_table));
2028        free(x);
2029    }
2030    active_method_table[active_method_count] = meth;
2031
2032    x = method_usage;
2033    method_usage = malloc((active_method_count + 1) * sizeof(*method_usage));
2034    if (!method_usage) {
2035        method_usage = x;
2036        return -ENOMEM;
2037    }
2038    if (active_method_count) {
2039        memcpy(method_usage, x, active_method_count * sizeof(*method_usage));
2040        free(x);
2041    }
2042    memset(&method_usage[active_method_count], 0, sizeof(*method_usage));
2043
2044    ++active_method_count;
2045
2046    /* initialize it */
2047    new_addr = 0;
2048    if (listen_addr) {
2049        new_addr = meth->method_addr_lookup(listen_addr);
2050        if (!new_addr) {
2051            gossip_err(
2052                "Error: failed to lookup listen address %s for method %s.\n",
2053                listen_addr, name);
2054            --active_method_count;
2055            return -EINVAL;
2056        }
2057        /* this is a bit of a hack */
2058        new_addr->method_type = active_method_count - 1;
2059    }
2060    ret = meth->initialize(new_addr, active_method_count - 1, flags);
2061    if (ret < 0) {
2062        gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
2063          "failed to initialize method %s.\n", name);
2064        --active_method_count;
2065        return ret;
2066    }
2067
2068    /* tell it about any open contexts */
2069    for (i=0; i<BMI_MAX_CONTEXTS; i++)
2070        if (context_array[i]) {
2071            ret = meth->open_context(i);
2072            if (ret < 0)
2073                break;
2074        }
2075
2076    return ret;
2077}
2078
2079 
2080int bmi_errno_to_pvfs(int error)
2081{
2082    int bmi_errno = error;
2083
2084#define __CASE(err)                      \
2085case -err: bmi_errno = -BMI_##err; break;\
2086case err: bmi_errno = BMI_##err; break
2087
2088    switch(error)
2089    {
2090        __CASE(EPERM);
2091        __CASE(ENOENT);
2092        __CASE(EINTR);
2093        __CASE(EIO);
2094        __CASE(ENXIO);
2095        __CASE(EBADF);
2096        __CASE(EAGAIN);
2097        __CASE(ENOMEM);
2098        __CASE(EFAULT);
2099        __CASE(EBUSY);
2100        __CASE(EEXIST);
2101        __CASE(ENODEV);
2102        __CASE(ENOTDIR);
2103        __CASE(EISDIR);
2104        __CASE(EINVAL);
2105        __CASE(EMFILE);
2106        __CASE(EFBIG);
2107        __CASE(ENOSPC);
2108        __CASE(EROFS);
2109        __CASE(EMLINK);
2110        __CASE(EPIPE);
2111        __CASE(EDEADLK);
2112        __CASE(ENAMETOOLONG);
2113        __CASE(ENOLCK);
2114        __CASE(ENOSYS);
2115        __CASE(ENOTEMPTY);
2116        __CASE(ELOOP);
2117        __CASE(ENOMSG);
2118        __CASE(ENODATA);
2119        __CASE(ETIME);
2120        __CASE(EREMOTE);
2121        __CASE(EPROTO);
2122        __CASE(EBADMSG);
2123        __CASE(EOVERFLOW);
2124        __CASE(EMSGSIZE);
2125        __CASE(EPROTOTYPE);
2126        __CASE(ENOPROTOOPT);
2127        __CASE(EPROTONOSUPPORT);
2128        __CASE(EOPNOTSUPP);
2129        __CASE(EADDRINUSE);
2130        __CASE(EADDRNOTAVAIL);
2131        __CASE(ENETDOWN);
2132        __CASE(ENETUNREACH);
2133        __CASE(ENETRESET);
2134        __CASE(ENOBUFS);
2135        __CASE(ETIMEDOUT);
2136        __CASE(ECONNREFUSED);
2137        __CASE(EHOSTDOWN);
2138        __CASE(EHOSTUNREACH);
2139        __CASE(EALREADY);
2140        __CASE(EACCES);
2141        __CASE(ECONNRESET);
2142#undef __CASE
2143    }
2144    return bmi_errno;
2145}
2146
2147/* bmi_check_forget_list()
2148 *
2149 * Scans queue of items that methods have suggested that we forget about
2150 *
2151 * no return value
2152 */
2153static void bmi_check_forget_list(void)
2154{
2155    BMI_addr_t tmp_addr;
2156    struct forget_item* tmp_item;
2157    ref_st_p tmp_ref = NULL;
2158   
2159    gen_mutex_lock(&forget_list_mutex);
2160    while(!qlist_empty(&forget_list))
2161    {
2162        tmp_item = qlist_entry(forget_list.next, struct forget_item,
2163            link);     
2164        qlist_del(&tmp_item->link);
2165        /* item is off of the list; unlock for a moment while we work on
2166         * this addr
2167         */
2168        gen_mutex_unlock(&forget_list_mutex);
2169        tmp_addr = tmp_item->addr;
2170        free(tmp_item);
2171
2172        gen_mutex_lock(&ref_mutex);
2173        tmp_ref = ref_list_search_addr(cur_ref_list, tmp_addr);
2174        if(tmp_ref && tmp_ref->ref_count == 0)
2175        {
2176            bmi_addr_drop(tmp_ref);
2177        }   
2178        gen_mutex_unlock(&ref_mutex);
2179
2180        gen_mutex_lock(&forget_list_mutex);
2181    }
2182    gen_mutex_unlock(&forget_list_mutex);
2183
2184    return;
2185}
2186
2187/* bmi_addr_drop
2188 *
2189 * Destroys a complete BMI address, including asking the method to clean up
2190 * its portion.  Will query the method for permission before proceeding
2191 *
2192 * NOTE: must be called with ref list mutex held
2193 */
2194static void bmi_addr_drop(ref_st_p tmp_ref)
2195{
2196    struct method_drop_addr_query query;
2197    query.response = 0;
2198    query.addr = tmp_ref->method_addr;
2199    int ret = 0;
2200
2201    /* reference count is zero; ask module if it wants us to discard
2202     * the address; TCP will tell us to drop addresses for which the
2203     * socket has died with no possibility of reconnect
2204     */
2205    ret = tmp_ref->interface->get_info(BMI_DROP_ADDR_QUERY,
2206        &query);
2207    if(ret == 0 && query.response == 1)
2208    {
2209        /* kill the address */
2210        gossip_debug(GOSSIP_BMI_DEBUG_CONTROL,
2211            "[BMI CONTROL]: %s: bmi discarding address: %llu\n",
2212            __func__, llu(tmp_ref->bmi_addr));
2213        ref_list_rem(cur_ref_list, tmp_ref->bmi_addr);
2214        /* NOTE: this triggers request to module to free underlying
2215         * resources if it wants to
2216         */
2217        dealloc_ref_st(tmp_ref);
2218    }
2219    return;
2220}
2221
2222/*
2223 * Local variables:
2224 *  c-indent-level: 4
2225 *  c-basic-offset: 4
2226 * End:
2227 *
2228 * vim: ts=8 sts=4 sw=4 expandtab
2229 */
Note: See TracBrowser for help on using the browser.