/*
 * nasd_core.c
 *
 * Core (memory) functionality for NASD EDRFS.
 *
 * Authors: Mathew Monroe, Nat Lanza
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_mem.h>
#include <nasd/linux/nasd_edrfs_client_linux.h>

NASD_DECLARE_ONCE(nasd_edrfs_data_init_once)
NASD_DECLARE_MUTEX(nasd_edrfs_data_use_counter_lock)

static int nasd_edrfs_data_use_counter;

nasd_freelist_t *nasd_edrfs_super_data_freelist = NULL;
#define NASD_MAX_FREE_SUPER_DATA  16
#define NASD_SUPER_DATA_INC       1
#define NASD_SUPER_DATA_INITIAL   1

nasd_edrfs_super_data_t *nasd_edrfs_get_super_data(void)
{
  nasd_edrfs_super_data_t *cp;
  NASD_FREELIST_GET(nasd_edrfs_super_data_freelist, cp, next,
                    (nasd_edrfs_super_data_t *));
  return cp;
}

void nasd_edrfs_free_super_data(nasd_edrfs_super_data_t  *cp)
{
  NASD_FREELIST_FREE(nasd_edrfs_super_data_freelist,cp,next);
}

nasd_status_t nasd_edrfs_super_data_init(void)
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_edrfs_super_data_freelist,
                       NASD_MAX_FREE_SUPER_DATA, NASD_SUPER_DATA_INC,
                       sizeof(nasd_edrfs_super_data_t));
  if (nasd_edrfs_super_data_freelist == NULL)
    return NASD_NO_MEM;
  NASD_FREELIST_PRIME(nasd_edrfs_super_data_freelist,
                      NASD_SUPER_DATA_INITIAL, next,
                      (nasd_edrfs_super_data_t *));
  return NASD_SUCCESS;
}

void nasd_edrfs_super_data_shutdown(void *ignored)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_super_data_freelist, next,
                        (nasd_edrfs_super_data_t *));
}

nasd_freelist_t *nasd_edrfs_readdir_data_freelist = NULL;
#define NASD_MAX_FREE_READDIR_DATA 16
#define NASD_READDIR_DATA_INC       2
#define NASD_READDIR_DATA_INITIAL   8

nasd_edrfs_readdir_data_t *nasd_edrfs_get_readdir_data(void)
{
  nasd_edrfs_readdir_data_t *cp;
  NASD_FREELIST_GET(nasd_edrfs_readdir_data_freelist, cp, next,
                    (nasd_edrfs_readdir_data_t *));
  return cp;
}

void nasd_edrfs_free_readdir_data(nasd_edrfs_readdir_data_t  *cp)
{
  NASD_FREELIST_FREE(nasd_edrfs_readdir_data_freelist,cp,next);
}

nasd_status_t nasd_edrfs_readdir_data_init(void)
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_edrfs_readdir_data_freelist,
                       NASD_MAX_FREE_READDIR_DATA, NASD_READDIR_DATA_INC,
                       sizeof(nasd_edrfs_readdir_data_t));
  if (nasd_edrfs_readdir_data_freelist == NULL)
    return NASD_NO_MEM;
  NASD_FREELIST_PRIME(nasd_edrfs_readdir_data_freelist,
                      NASD_READDIR_DATA_INITIAL, next,
                      (nasd_edrfs_readdir_data_t *));
  return NASD_SUCCESS;
}

void nasd_edrfs_readdir_data_shutdown(void *ignored)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_readdir_data_freelist, next,
                        (nasd_edrfs_readdir_data_t *));
}


nasd_freelist_t *nasd_edrfs_create_data_freelist = NULL;
#define NASD_MAX_FREE_CREATE_DATA 16
#define NASD_CREATE_DATA_INC       2
#define NASD_CREATE_DATA_INITIAL   8

nasd_edrfs_create_data_t *nasd_edrfs_get_create_data(void)
{
  nasd_edrfs_create_data_t *cp;
  NASD_FREELIST_GET(nasd_edrfs_create_data_freelist, cp, next,
                    (nasd_edrfs_create_data_t *));
  return cp;
}

void nasd_edrfs_free_create_data(nasd_edrfs_create_data_t  *cp)
{
  NASD_FREELIST_FREE(nasd_edrfs_create_data_freelist,cp,next);
}

nasd_status_t nasd_edrfs_create_data_init(void)
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_edrfs_create_data_freelist,
                       NASD_MAX_FREE_CREATE_DATA, NASD_CREATE_DATA_INC,
                       sizeof(nasd_edrfs_create_data_t));
  if (nasd_edrfs_create_data_freelist == NULL)
    return NASD_NO_MEM;
  NASD_FREELIST_PRIME(nasd_edrfs_create_data_freelist,
                      NASD_CREATE_DATA_INITIAL, next,
                      (nasd_edrfs_create_data_t *));
  return NASD_SUCCESS;
}

void nasd_edrfs_create_data_shutdown(void *ignored)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_create_data_freelist, next,
                        (nasd_edrfs_create_data_t *));
}



nasd_freelist_t *nasd_edrfs_rename_data_freelist = NULL;
#define NASD_MAX_FREE_RENAME_DATA 16
#define NASD_RENAME_DATA_INC       2
#define NASD_RENAME_DATA_INITIAL   8

nasd_edrfs_rename_data_t *nasd_edrfs_get_rename_data(void)
{
  nasd_edrfs_rename_data_t *cp;
  NASD_FREELIST_GET(nasd_edrfs_rename_data_freelist, cp, next,
                    (nasd_edrfs_rename_data_t *));
  return cp;
}

void nasd_edrfs_free_rename_data(nasd_edrfs_rename_data_t  *cp)
{
  NASD_FREELIST_FREE(nasd_edrfs_rename_data_freelist,cp,next);
}

nasd_status_t nasd_edrfs_rename_data_init(void)
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_edrfs_rename_data_freelist,
                       NASD_MAX_FREE_RENAME_DATA, NASD_RENAME_DATA_INC,
                       sizeof(nasd_edrfs_rename_data_t));
  if (nasd_edrfs_rename_data_freelist == NULL)
    return NASD_NO_MEM;
  NASD_FREELIST_PRIME(nasd_edrfs_rename_data_freelist,
                      NASD_RENAME_DATA_INITIAL, next,
                      (nasd_edrfs_rename_data_t *));
  return NASD_SUCCESS;
}

void nasd_edrfs_rename_data_shutdown(void *ignored)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_rename_data_freelist, next,
                        (nasd_edrfs_rename_data_t *));
}


nasd_freelist_t *nasd_edrfs_lookup_data_freelist = NULL;
#define NASD_MAX_FREE_LOOKUP_DATA 16
#define NASD_LOOKUP_DATA_INC       2
#define NASD_LOOKUP_DATA_INITIAL   8

nasd_edrfs_lookup_data_t *nasd_edrfs_get_lookup_data(void)
{
  nasd_edrfs_lookup_data_t *cp;
  NASD_FREELIST_GET(nasd_edrfs_lookup_data_freelist, cp, next,
                    (nasd_edrfs_lookup_data_t *));
  return cp;
}

void nasd_edrfs_free_lookup_data(nasd_edrfs_lookup_data_t  *cp)
{
  NASD_FREELIST_FREE(nasd_edrfs_lookup_data_freelist,cp,next);
}

nasd_status_t nasd_edrfs_lookup_data_init(void)
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_edrfs_lookup_data_freelist,
                       NASD_MAX_FREE_LOOKUP_DATA, NASD_LOOKUP_DATA_INC,
                       sizeof(nasd_edrfs_lookup_data_t));
  if (nasd_edrfs_lookup_data_freelist == NULL)
    return NASD_NO_MEM;
  NASD_FREELIST_PRIME(nasd_edrfs_lookup_data_freelist,
                      NASD_LOOKUP_DATA_INITIAL, next,
                      (nasd_edrfs_lookup_data_t *));
  return NASD_SUCCESS;
}

void nasd_edrfs_lookup_data_shutdown(void *ignored)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_lookup_data_freelist, next,
                        (nasd_edrfs_lookup_data_t *));
}


nasd_freelist_t *nasd_edrfs_mkdir_data_freelist = NULL;
#define NASD_MAX_FREE_MKDIR_DATA 16
#define NASD_MKDIR_DATA_INC       2
#define NASD_MKDIR_DATA_INITIAL   8

nasd_edrfs_mkdir_data_t *nasd_edrfs_get_mkdir_data(void)
{
  nasd_edrfs_mkdir_data_t *cp;
  NASD_FREELIST_GET(nasd_edrfs_mkdir_data_freelist, cp, next,
                    (nasd_edrfs_mkdir_data_t *));
  return cp;
}

void nasd_edrfs_free_mkdir_data(nasd_edrfs_mkdir_data_t  *cp)
{
  NASD_FREELIST_FREE(nasd_edrfs_mkdir_data_freelist,cp,next);
}

nasd_status_t nasd_edrfs_mkdir_data_init(void)
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_edrfs_mkdir_data_freelist,
                       NASD_MAX_FREE_MKDIR_DATA, NASD_MKDIR_DATA_INC,
                       sizeof(nasd_edrfs_mkdir_data_t));
  if (nasd_edrfs_mkdir_data_freelist == NULL)
    return NASD_NO_MEM;
  NASD_FREELIST_PRIME(nasd_edrfs_mkdir_data_freelist,
                      NASD_MKDIR_DATA_INITIAL, next,
                      (nasd_edrfs_mkdir_data_t *));
  return NASD_SUCCESS;
}

void nasd_edrfs_mkdir_data_shutdown(void *ignored)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_mkdir_data_freelist, next,
                        (nasd_edrfs_mkdir_data_t *));
}


nasd_freelist_t *nasd_edrfs_notify_data_freelist = NULL;
#define NASD_MAX_FREE_NOTIFY_DATA 16
#define NASD_NOTIFY_DATA_INC       2
#define NASD_NOTIFY_DATA_INITIAL   8

nasd_edrfs_notify_data_t *nasd_edrfs_get_notify_data(void)
{
  nasd_edrfs_notify_data_t *cp;
  NASD_FREELIST_GET(nasd_edrfs_notify_data_freelist, cp, next,
                    (nasd_edrfs_notify_data_t *));
  return cp;
}

void nasd_edrfs_free_notify_data(nasd_edrfs_notify_data_t  *cp)
{
  NASD_FREELIST_FREE(nasd_edrfs_notify_data_freelist,cp,next);
}

nasd_status_t nasd_edrfs_notify_data_init(void)
{
  NASD_FREELIST_CREATE(nasd_edrfs_notify_data_freelist,
                       NASD_MAX_FREE_NOTIFY_DATA, NASD_NOTIFY_DATA_INC,
                       sizeof(nasd_edrfs_notify_data_t));
  if (nasd_edrfs_notify_data_freelist == NULL)
    return NASD_NO_MEM;
  NASD_FREELIST_PRIME(nasd_edrfs_notify_data_freelist,
                      NASD_NOTIFY_DATA_INITIAL, next,
                      (nasd_edrfs_notify_data_t *));
  return NASD_SUCCESS;
}

void nasd_edrfs_notify_data_shutdown(void *ignored)
{
  NASD_FREELIST_DESTROY(nasd_edrfs_notify_data_freelist, next,
                        (nasd_edrfs_notify_data_t *));
}


nasd_freelist_t *nasd_edrfs_kiobuf_data_freelist = NULL;
#define NASD_MAX_FREE_KIOBUF_DATA 8
#define NASD_KIOBUF_DATA_INC      2
#define NASD_KIOBUF_DATA_INITIAL  4


nasd_status_t nasd_edrfs_init_kiobuf_data(nasd_edrfs_kiobuf_data_t *cp) {
  int i, err;

  /* thread the static bits of the memlist */
  for (i = 1; i < NASD_EDRFS_MAX_EMBEDDED_MEMLIST; i++) {
    cp->memlist[i-1].next = &(cp->memlist[i]);
  }

  cp->memlist[NASD_EDRFS_MAX_EMBEDDED_MEMLIST-1].next = NULL;
  cp->memlist_alloc = NULL;
  cp->num_alloc = 0;

  err = alloc_kiovec(1, &(cp->iobuf));
  if (err) { return NASD_FAIL; }

  return NASD_SUCCESS;
}

void nasd_edrfs_clean_kiobuf_data(nasd_edrfs_kiobuf_data_t *cp) {
  free_kiovec(1, &(cp->iobuf));

  if (cp->num_alloc != 0) {
    NASD_Free(cp->memlist_alloc, cp->num_alloc * sizeof(nasd_mem_list_t));
    cp->memlist_alloc = NULL;
    cp->memlist[NASD_EDRFS_MAX_EMBEDDED_MEMLIST-1].next = NULL;    
    cp->num_alloc = 0;
  }
}


nasd_edrfs_kiobuf_data_t *nasd_edrfs_get_kiobuf_data(void) {
  nasd_edrfs_kiobuf_data_t *cp;
  NASD_FREELIST_GET_INIT(nasd_edrfs_kiobuf_data_freelist, cp, next,
                         (nasd_edrfs_kiobuf_data_t *),
                         nasd_edrfs_init_kiobuf_data);
  return cp;
}


void nasd_edrfs_free_kiobuf_data(nasd_edrfs_kiobuf_data_t *cp) {
  NASD_FREELIST_FREE_CLEAN(nasd_edrfs_kiobuf_data_freelist,cp,next,
                           nasd_edrfs_clean_kiobuf_data);
}
  

nasd_status_t nasd_edrfs_kiobuf_data_init(void) {
  NASD_FREELIST_CREATE(nasd_edrfs_kiobuf_data_freelist,
                       NASD_MAX_FREE_KIOBUF_DATA, NASD_KIOBUF_DATA_INC,
                       sizeof(nasd_edrfs_kiobuf_data_t));
  if (nasd_edrfs_kiobuf_data_freelist == NULL)
    return NASD_NO_MEM;
  NASD_FREELIST_PRIME_INIT(nasd_edrfs_kiobuf_data_freelist,
                           NASD_KIOBUF_DATA_INITIAL, next,
                           (nasd_edrfs_kiobuf_data_t *),
                           nasd_edrfs_init_kiobuf_data);
  return NASD_SUCCESS;
}


void nasd_edrfs_kiobuf_data_shutdown(void *ignored) {
  NASD_FREELIST_DESTROY_CLEAN(nasd_edrfs_kiobuf_data_freelist, next,
                              (nasd_edrfs_kiobuf_data_t *),
                              nasd_edrfs_clean_kiobuf_data);
}


static void nasd_edrfs_data_sys_init(void) {
  nasd_status_t rc;

  rc = nasd_mutex_init(&nasd_edrfs_data_use_counter_lock);
  if (rc) { NASD_PANIC(); }

  nasd_edrfs_data_use_counter = 0;
}

static nasd_status_t nasd_edrfs_data_real_init(void) {
  nasd_status_t rc;

#if DEBUG_INIT_DETAIL
  nasd_printf("  initializing edrfs client super data freelist...\n");
#endif /* DEBUG_READ_SUPER_DETAIL */
  rc = nasd_edrfs_super_data_init();
  if (rc) {
    nasd_printf("EDRFS: INIT can't init NASD super data freelist, rc=0x%x\n", rc);
    return NASD_NO_MEM;
  }

#if DEBUG_INIT_DETAIL
  nasd_printf("  initializing edrfs client readdir data freelist...\n");
#endif /* DEBUG_INIT_DETAIL */
  rc = nasd_edrfs_readdir_data_init();
  if (rc) {
    nasd_printf("EDRFS: read_readdir can't init NASD readdir data freelist, rc=0x%x\n", rc);
    return NASD_NO_MEM;
  }

#if DEBUG_INIT_DETAIL
  nasd_printf("  initializing edrfs client lookup data freelist...\n");
#endif /* DEBUG_INIT_DETAIL */
  rc = nasd_edrfs_lookup_data_init();
  if (rc) {
    nasd_printf("EDRFS: read_lookup can't init NASD lookup data freelist, rc=0x%x\n", rc);
    return NASD_NO_MEM;
  }

#if DEBUG_INIT_DETAIL
  nasd_printf("  initializing edrfs client mkdir data freelist...\n");
#endif /* DEBUG_INIT_DETAIL */
  rc = nasd_edrfs_mkdir_data_init();
  if (rc) {
    nasd_printf("EDRFS: read_mkdir can't init NASD mkdir data freelist, rc=0x%x\n", rc);
    return NASD_NO_MEM;
  }

#if DEBUG_INIT_DETAIL
  nasd_printf("  initializing edrfs client create data freelist...\n");
#endif /* DEBUG_INIT_DETAIL */
  rc = nasd_edrfs_create_data_init();
  if (rc) {
    nasd_printf("EDRFS: read_create can't init NASD create data freelist, rc=0x%x\n", rc);
    return NASD_NO_MEM;
  }

#if DEBUG_INIT_DETAIL
  nasd_printf("  initializing edrfs client rename data freelist...\n");
#endif /* DEBUG_INIT_DETAIL */
  rc = nasd_edrfs_rename_data_init();
  if (rc) {
    nasd_printf("EDRFS: read_rename can't init NASD rename data freelist, rc=0x%x\n", rc);
    return NASD_NO_MEM;
  }

#if DEBUG_INIT_DETAIL
  nasd_printf("  initializing edrfs client notify data freelist...\n");
#endif /* DEBUG_INIT_DETAIL */
  rc = nasd_edrfs_notify_data_init();
  if (rc) {
    nasd_printf("EDRFS: read_notify can't init NASD notify data freelist, rc=0x%x\n", rc);
    return NASD_NO_MEM;
  }

#if DEBUG_INIT_DETAIL
  nasd_printf("  initializing edrfs client kiobuf data freelist...\n");
#endif /* DEBUG_INIT_DETAIL */
  rc = nasd_edrfs_kiobuf_data_init();
  if (rc) {
    nasd_printf("EDRFS: read_notify can't init NASD kiobuf data freelist, rc=0x%x\n", rc);
    return NASD_NO_MEM;
  }

  
  return NASD_SUCCESS;
}

void nasd_edrfs_data_real_shutdown(void){
  nasd_edrfs_super_data_shutdown(NULL);
  nasd_edrfs_readdir_data_shutdown(NULL);
  nasd_edrfs_create_data_shutdown(NULL);
  nasd_edrfs_rename_data_shutdown(NULL);
  nasd_edrfs_lookup_data_shutdown(NULL);
  nasd_edrfs_mkdir_data_shutdown(NULL);
  nasd_edrfs_notify_data_shutdown(NULL);
  nasd_edrfs_kiobuf_data_shutdown(NULL);
}

nasd_status_t nasd_edrfs_data_init(void) {
  nasd_status_t rc;

  nasd_once(&nasd_edrfs_data_init_once, nasd_edrfs_data_sys_init);

  NASD_LOCK_MUTEX(nasd_edrfs_data_use_counter_lock);

  nasd_edrfs_data_use_counter++;

  if (nasd_edrfs_data_use_counter == 1) {
    if ((rc = nasd_edrfs_data_real_init())) {
      nasd_edrfs_data_use_counter = 0;
      nasd_edrfs_data_real_shutdown();
    }
  } else {
    rc = NASD_SUCCESS;
  }

  NASD_UNLOCK_MUTEX(nasd_edrfs_data_use_counter_lock);
  return rc;
}

void nasd_edrfs_data_shutdown() {
  NASD_ASSERT(nasd_edrfs_data_use_counter != 0);
  NASD_LOCK_MUTEX(nasd_edrfs_data_use_counter_lock);

  nasd_edrfs_data_use_counter--;

  if (nasd_edrfs_data_use_counter == 0) {
    nasd_edrfs_data_real_shutdown();
  }

  NASD_UNLOCK_MUTEX(nasd_edrfs_data_use_counter_lock);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
