#ifdef not
static char rcsid[] = "$Id: ompsm_data.c,v 1.21 2003/09/26 13:04:39 a-hasega Exp $";
#endif

#include "ompsm.h"

#define CHUNK_SIZE  256
#define MAP_CHUNK_SIZE 256

struct global_data_entry {
    void **p;
    int size;
    void *data_p;
};

struct global_data_chunk {
    struct global_data_chunk *link;
    int n_data;
    struct global_data_entry data[CHUNK_SIZE];
};

static struct global_data_chunk *_ompsm_gdata_ctop = NULL;
static struct global_data_chunk *_ompsm_gdata_cp = NULL;

int _ompsm_gdata_size;
int _ompsm_gdata_count;
int _ompsm_free_shmem_size = OMPSM_DEFAULT_FREE_MEM_SIZE;

struct data_map_chunk {
    struct data_map_chunk *link;
    int n_ent;
    struct data_map_entry map_ent[MAP_CHUNK_SIZE];
};

static struct data_map_chunk *_ompsm_map_ctop = NULL;
static struct data_map_chunk *_ompsm_map_cp = NULL;
static struct data_map_entry *_ompsm_map_mp = NULL;

/*
 * shared data management:
 */
/* called from _G_INIT_DATA in .ctors section */
/*  'p' points to the pointer variable to shared data.
 *  'size' is the size of shared data. 
 *  'data_p' is the pointer to the initialized data.
 */
void _shm_data_init(void **p,int size,void *data_p)
{
    struct global_data_chunk *cp;

    if(_ompsm_gdata_cp == NULL ||
       (_ompsm_gdata_cp->n_data >= CHUNK_SIZE)){
	if((cp = (struct global_data_chunk *)malloc(sizeof(*cp))) == NULL){
	    _ompc_fatal("cannot alloc global_data_chunk");
	}
	bzero(cp,sizeof(*cp));
	if(_ompsm_gdata_ctop == NULL) _ompsm_gdata_ctop = cp;
	if(_ompsm_gdata_cp != NULL) _ompsm_gdata_cp->link = cp;
	_ompsm_gdata_cp = cp;
    }
    
    cp = _ompsm_gdata_cp;
    cp->data[cp->n_data].p = p;
    cp->data[cp->n_data].size = size;
    cp->data[cp->n_data].data_p = data_p;
    cp->n_data++;
}

struct data_map_entry *map_data_entry_lookup(void **p,int offset)
{
    struct data_map_chunk *cp;
    struct data_map_entry *mp;
    int k;

    if(_ompsm_map_ctop != NULL){
	for(cp = _ompsm_map_ctop; cp != NULL; cp = cp->link){
	    for(k = 0; k < cp->n_ent; k++){
		mp = &(cp->map_ent[k]);
		if(mp->p == p && mp->offset == offset) return mp;
	    }
	}
    }
    if(_ompsm_map_cp == NULL ||
       (_ompsm_map_cp->n_ent >= MAP_CHUNK_SIZE)){
	if((cp = (struct data_map_chunk *)malloc(sizeof(*cp))) == NULL){
	    _ompc_fatal("cannot alloc data_map_chunk");
	}
	bzero(cp,sizeof(*cp));
	if(_ompsm_map_ctop == NULL) _ompsm_map_ctop = cp;
	if(_ompsm_map_cp != NULL) _ompsm_map_cp->link = cp;
	_ompsm_map_cp = cp;
    }
    cp = _ompsm_map_cp;
    mp = &(cp->map_ent[cp->n_ent++]);
    mp->p = p;
    mp->offset = offset;
    return mp;
}

/* specification of data mapping */
void _map_data_init(void **p,int offset,int size, int n_dim)
{
    struct data_map_entry *mp;
    mp = map_data_entry_lookup(p,offset);
    if(mp->n_dim != 0){
	_ompsm_map_mp = NULL;
	return;  /* already defined previously */
    }
    mp->size = size;
    mp->n_dim = n_dim;
    _ompsm_map_mp = mp;
}

void _map_data_dim(int dim, int size)
{
    if(_ompsm_map_mp == NULL) return;
    _ompsm_map_mp->dim_size[dim] = size;
}

void _map_data_dist(int dist_dim,int mode,int blk_size,int scale,int offset)
{
    struct data_map_entry *mp;
    if((mp = _ompsm_map_mp) == NULL) return;
    mp->dist_dim = dist_dim;
    mp->dist_mode = mode;
    mp->dist_blk_size = blk_size;
    mp->dist_scale = scale;
    mp->dist_offset = offset;
}

void _map_data_dist_default(void **p,int offset,int size)
{
    struct data_map_entry *mp;
    mp = map_data_entry_lookup(p,offset);
    if(mp->n_dim != 0) return;
    mp->dist_mode = MAP_NONE;
    if(mp->size == 0) mp->size = size;
}

/* estimate global data size */
void _ompsm_estimate_shmem_size()
{
    struct global_data_chunk *cp;
    struct global_data_entry *ep;
    int i,s,ss;

    /* align and find the max size */
    for(cp = _ompsm_gdata_ctop; cp != NULL; cp = cp->link){
	for(i = 0; i < cp->n_data; i++){
	    ep = &(cp->data[i]);
	    s = OMPSM_ALIGN_SIZE(ep->size);
	    ss = *((int *)(ep->p));
	    if(s > ss) *((int *)(ep->p)) = s;
	}
    }

    /* making all size consistent */
    for(cp = _ompsm_gdata_ctop; cp != NULL; cp = cp->link){
	for(i = 0; i < cp->n_data; i++){
	    ep = &(cp->data[i]);
	    ep->size = *((int *)(ep->p));
	}
    }

    /* clean up, and count */
    _ompsm_gdata_size = 0;
    _ompsm_gdata_count = 0;
    for(cp = _ompsm_gdata_ctop; cp != NULL; cp = cp->link){
	for(i = 0; i < cp->n_data; i++){
	    ep = &(cp->data[i]);
	    if((s = *((int *)(ep->p))) != 0){
		_ompsm_gdata_size += s;
		*((int *)(ep->p)) = 0;  /* clean up */
		_ompsm_gdata_count++;
	    }
	}
    }
}



/* allocate shared memory area, and set home node */
void
_ompsm_allocate_global_data ()
{
  struct global_data_chunk	*cp;
  struct global_data_entry	*ep;
  int	i;

  void	*p = NULL;
  int	count = 0;


  if (_ompc_debug_flag) {
    fprintf (stderr, "id=%d:allocate_global_data __G__=0x%lx...\n",
	     _ompc_node_id,(unsigned long int)__G__);
  }

  for (cp = _ompsm_gdata_ctop; cp != NULL; cp = cp->link) {
    for (i = 0; i < cp->n_data; i++) {
      ep = &(cp->data[i]);
      if (*(ep->p) == NULL) {
	p = (void *) OMPSM_ALLOC (ep->size);
	if (p == NULL) {
	  _ompc_fatal ("_ompsm_allocate_global_data : can not allocate global memory");
	}
	*(ep->p) = p;
	count ++;
#ifdef not /* USE_SCASH */
	if (_ompc_scash_dist_flag) {
	  _ompsm_scash_default_block_dist (p, ep->size);
	}
#endif
      } else {
	ep->size   = 0;	
	ep->data_p = NULL;
      }
    }

    if (count != _ompsm_gdata_count) {
      fprintf (stderr, "id=%d:count=%d, _ompsm_gdata_count=%d\n",
	       _ompc_node_id, count, _ompsm_gdata_count);
      _ompc_fatal ("_ompsm_allocate_global_data failed");
    }

#ifdef USE_SCASH
    {
      struct data_map_chunk *mcp;
      struct data_map_entry *mp;

      /* only default block mapping */
      for(mcp = _ompsm_map_ctop; mcp != NULL; mcp = mcp->link){
	for(i = 0; i < mcp->n_ent; i++){
	  mp = &(mcp->map_ent[i]);
	  _ompsm_scash_data_dist(mp);
	}
      }
    }
#endif
  }

  if (_ompc_debug_flag) {
    fprintf (stderr, "id=%d:allocate_global_data end ...\n",
	     _ompc_node_id);
  }
}


/* distribute global memory address to node, and initialize it */
void
_ompsm_init_global_data ()
{
  struct global_data_chunk	*cp;
  struct global_data_entry	*ep;
  void	***dpp, **dp;
  int	i,count;


  if (_ompc_debug_flag) {
    fprintf (stderr, "id=%d:init_global_data __G__=0x%lx...\n",
		_ompc_node_id,(unsigned long int)__G__);
  }

  if (IS_MASTER_NODE) {
    dpp = (void ***) OMPSM_ALLOC (sizeof(void **)*_ompsm_gdata_count);
    if (dpp == NULL) {
      _ompc_fatal ("_ompsm_init_global_data : can not allocate global memory");
    }
    dp = (void **) OMPSM_ALLOC(sizeof(void *)*_ompsm_gdata_count);
    if (dp == NULL) {
      _ompc_fatal ("_ompsm_init_global_lock : can not allocate global memory");
    }
    __G__->gdata_pp = dpp;
    __G__->gdata_p = dp;

    count = 0;
    /* allocate global data */
    for (cp = _ompsm_gdata_ctop; cp != NULL; cp = cp->link) {
      for (i = 0; i < cp->n_data; i++) {
	ep = &(cp->data[i]);
	if (ep->size != 0) {
	  *dpp++ = ep->p;
	  *dp++ = *(ep->p);
	  count++;

	  /* copy initialized data */
	  if(ep->data_p != NULL) {
	    bcopy (ep->data_p, *(ep->p), ep->size);
	  }
	} 
      }
    }
    if(count != _ompsm_gdata_count) {
      fprintf(stderr, "count=%d, _ompsm_gdata_count=%d\n",
	      count,_ompsm_gdata_count);
      _ompc_fatal("_ompsm_init_global_data failed");
    }
    __G__->gdata_n = _ompsm_gdata_count;

    OMPSM_BARRIER ();

  } else {
    OMPSM_BARRIER ();

    count = __G__->gdata_n;
    dpp = __G__->gdata_pp;
    dp = __G__->gdata_p;
    for(i = 0; i < count; i++) {
      *(*dpp++) = *dp++;
    }
  }

  if(_ompc_debug_flag) {
    fprintf (stderr, "id=%d:init_global_data end ...\n",
	     _ompc_node_id);
  }
}
