/*
 *------------------------------------------------------------------
 * svm.h - shared VM allocation, mmap(...MAP_FIXED...)
 * brain police
 *
 * Copyright (c) 2009 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *------------------------------------------------------------------
 */

#ifndef __included_svm_h__
#define __included_svm_h__

#include <pthread.h>
#include <vppinfra/clib.h>
#include <vppinfra/mem.h>

#define MMAP_PAGESIZE (clib_mem_get_page_size())

#define SVM_VERSION ((1<<16) | 1)	/* set to declare region ready. */

#define SVM_FLAGS_MHEAP (1<<0)	/* region contains an mheap */
#define SVM_FLAGS_FILE  (1<<1)	/* region backed by one or more files */
#define SVM_FLAGS_NODATA (1<<2)	/* region will be further subdivided */
#define SVM_FLAGS_NEED_DATA_INIT (1<<3)

#define SVM_PVT_MHEAP_SIZE (128<<10)	/* region's private mheap (128k) */

typedef struct svm_region_
{
  volatile uword version;
  pthread_mutex_t mutex;
  pthread_cond_t condvar;
  int mutex_owner_pid;		/* in case of trouble */
  int mutex_owner_tag;
  uword flags;
  uword virtual_base;		/* base of the region object */
  uword virtual_size;
  void *region_heap;
  void *data_base;		/* data portion base address */
  void *data_heap;		/* data heap, if any */
  volatile void *user_ctx;	/* user context pointer */
  /* stuff allocated in the region's heap */
  uword bitmap_size;		/* nbits in virtual alloc bitmap */
  uword *bitmap;		/* the bitmap */
  char *region_name;
  char *backing_file;
  char **filenames;
  uword *client_pids;
  /* pad */

  /* next page:
   * (64K) clib heap for the region itself
   *
   * data_base -> whatever is in this region
   */

} svm_region_t;

typedef struct svm_map_region_args_
{
  char *root_path;		/* NULL means use the truly global arena */
  char *name;
  u64 baseva;
  u64 size;
  u64 pvt_heap_size;
  uword flags;
  char *backing_file;
  uword backing_mmap_size;
  /* uid, gid to own the svm region(s) */
  int uid;
  int gid;
} svm_map_region_args_t;


/*
 * Memory shared across all router instances. Packet buffers, etc
 * Base should be "out of the way," and size should be big enough to
 * cover everything we plan to put here.
 */
#define SVM_GLOBAL_REGION_BASEVA  0x30000000
#define SVM_GLOBAL_REGION_SIZE    (64<<20)
#define SVM_GLOBAL_REGION_NAME "/global_vm"

/*
 * Memory shared across individual router instances.
 */
#define SVM_OVERLAY_REGION_BASEVA \
               (SVM_GLOBAL_REGION_BASEVA + SVM_GLOBAL_REGION_SIZE)
#define SVM_OVERLAY_REGION_SIZE   (1<<20)
#define SVM_OVERLAY_REGION_BASENAME "/overlay_vm"

typedef struct
{
  u8 *subregion_name;
} svm_subregion_t;

typedef struct
{
  svm_subregion_t *subregions;	/* subregion pool */
  uword *name_hash;
  u8 *root_path;
} svm_main_region_t;


void *svm_region_find_or_create (svm_map_region_args_t * a);
void svm_region_init (void);
void svm_region_init_chroot (char *root_path);
void svm_region_init_chroot_uid_gid (char *root_path, int uid, int gid);
void svm_region_init_args (svm_map_region_args_t * a);
void svm_region_exit (void);
void svm_region_unmap (void *rp_arg);
void svm_client_scan (char *root_path);
void svm_client_scan_this_region_nolock (svm_region_t * rp);
u8 *shm_name_from_svm_map_region_args (svm_map_region_args_t * a);

static inline void *
svm_mem_alloc (svm_region_t * rp, uword size)
{
  u8 *oldheap;
  ASSERT (rp->flags & SVM_FLAGS_MHEAP);
  u8 *rv;

  pthread_mutex_lock (&rp->mutex);
  oldheap = clib_mem_set_heap (rp->data_heap);
  rv = clib_mem_alloc (size);
  clib_mem_set_heap (oldheap);
  pthread_mutex_unlock (&rp->mutex);
  return (rv);
}

static inline void *
svm_mem_alloc_aligned_at_offset (svm_region_t * rp,
				 uword size, uword align, uword offset)
{
  u8 *oldheap;
  ASSERT (rp->flags & SVM_FLAGS_MHEAP);
  u8 *rv;

  pthread_mutex_lock (&rp->mutex);
  oldheap = clib_mem_set_heap (rp->data_heap);
  rv = clib_mem_alloc_aligned_at_offset (size, align, offset,
					 1 /* yes, call os_out_of_memory */ );
  clib_mem_set_heap (oldheap);
  pthread_mutex_unlock (&rp->mutex);
  return (rv);
}

static inline void
svm_mem_free (svm_region_t * rp, void *ptr)
{
  u8 *oldheap;
  ASSERT (rp->flags & SVM_FLAGS_MHEAP);

  pthread_mutex_lock (&rp->mutex);
  oldheap = clib_mem_set_heap (rp->data_heap);
  clib_mem_free (ptr);
  clib_mem_set_heap (oldheap);
  pthread_mutex_unlock (&rp->mutex);

}

static inline void *
svm_push_pvt_heap (svm_region_t * rp)
{
  u8 *oldheap;
  oldheap = clib_mem_set_heap (rp->region_heap);
  return ((void *) oldheap);
}

static inline void *
svm_push_data_heap (svm_region_t * rp)
{
  u8 *oldheap;
  oldheap = clib_mem_set_heap (rp->data_heap);
  return ((void *) oldheap);
}

static inline void
svm_pop_heap (void *oldheap)
{
  clib_mem_set_heap (oldheap);
}

u8 *format_svm_region (u8 * s, va_list * args);

svm_region_t *svm_get_root_rp (void);

#endif /* __included_svm_h__ */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */