/*
 * nasd_edrfs_ops.c
 *
 * RPC management for NASD EDRFS server.
 *
 * Author: Jim Zelenka
 */
/*
 * 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_types.h>
#include <nasd/nasd_edrfs_types.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_edrfs_server_internal.h>
#include <nasd/nasd_edrfs_server.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_control.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_marshall.h>
#include <nasd/nasd_edrfs_rpc.h>
#include <nasd/nasd_timeout.h>

#include <nasd/nasd_pipe.h>

#include <sys/errno.h>

nasd_uint64 nasd_edrfs_threads_in_rpc = 0;
int nasd_edrfs_wait_shutdown = 0;
NASD_DECLARE_COND(nasd_edrfs_shutdown_cond)
NASD_DECLARE_MUTEX(nasd_edrfs_rpc_cnt_lock)
NASD_DECLARE_MUTEX(nasd_edrfs_rpc_qdepth_lock)

int nasd_edrfs_active = 0;

int nasd_edrfs_rpc_qdepth = 0;
nasd_timer_t nasd_edrfs_rpc_qdepth_timer;
nasd_edrfs_rpc_opdepths_t nasd_edrfs_rpc_opdepths;

nasd_uint64 nasd_edrfs_shutdown_timeouts = 0;
nasd_timeout_handle_t nasd_edrfs_shutdown_timeout_handle;

nasd_edrfs_opstats_t nasd_edrfs_opstats;
nasd_edrfs_cachestats_t nasd_edrfs_cache_stats;

nasd_timespec_t nasd_edrfs_shutdown_timeout_interval = {1, 0};

void
nasd_edrfs_begin_rpc(
  nasd_opstat_t  *opstat)
{
  NASD_ATOMIC_INC64(&nasd_edrfs_threads_in_rpc);
}

void
nasd_edrfs_end_rpc(
  nasd_opstat_t  *opstat)
{
  NASD_ATOMIC_DEC64(&nasd_edrfs_threads_in_rpc);
  if (nasd_edrfs_wait_shutdown && (nasd_edrfs_threads_in_rpc == 0)) {
    NASD_BROADCAST_COND(nasd_edrfs_shutdown_cond);
  }
}

void
nasd_edrfs_dump_opstat(
  nasd_opstat_t  *os,
  char           *name)
{
  unsigned long s, ns;

  s = os->op_nsecs / 1000000000UL;
  ns = os->op_nsecs % 1000000000UL;
  nasd_printf("Operation: %s\n", name);
  nasd_printf("  time       %lu:%09lu\n", s, ns);
  nasd_printf("  num_ops    %5" NASD_64u_FMT "\n", os->num_ops);
  nasd_printf("  in_flight  %5" NASD_64u_FMT "\n", os->in_flight);
  nasd_printf("  invalid    %5" NASD_64u_FMT "\n", os->invalid);
}

#define DUMP_OPSTAT(_opname_) \
  nasd_edrfs_dump_opstat(&nasd_edrfs_opstats._opname_,NASD_STRING(_opname_))

#define NASD_EDRFS_CSDUMP(_stat_) { \
  nasd_printf("  %24s %5lu\n", NASD_STRING(_stat_), nasd_edrfs_cache_stats._stat_); \
}

void
nasd_edrfs_timeout_shutdown_rpc(
  nasd_timeout_handle_t   tmh,
  void                   *arg1,
  void                   *arg2)
{
  nasd_timespec_t ts;
  char str[128];

#if NASD_EDRFS_RPC_DEBUG_RPC_SHUTDOWN > 0
  nasd_gettime(&ts);
  nasd_printf("EDRFS: shutdown timeout at %s, nasd_edrfs_threads_in_rpc=%"
    NASD_64u_FMT "\n", nasd_timestr_r(ts, str), nasd_edrfs_threads_in_rpc);
#endif /* NASD_EDRFS_RPC_DEBUG_RPC_SHUTDOWN > 0 */
  if (nasd_edrfs_threads_in_rpc == 0) {
    NASD_BROADCAST_COND(nasd_edrfs_shutdown_cond);
  }
  nasd_edrfs_shutdown_timeouts++;
}

void
nasd_edrfs_wait_shutdown_rpc()
{
  nasd_status_t rc;

  nasd_edrfs_wait_shutdown = 1;

  rc = NASD_FAIL;

  NASD_LOCK_MUTEX(nasd_edrfs_rpc_cnt_lock);
  while(nasd_edrfs_threads_in_rpc) {
    if (rc) {
      rc = nasd_timeout_add(&nasd_edrfs_shutdown_timeout_handle,
        nasd_edrfs_timeout_shutdown_rpc, NULL, NULL,
        nasd_edrfs_shutdown_timeout_interval,
        nasd_edrfs_shutdown_timeout_interval,
        NASD_TIMEOUT_F_PERIODIC);
    }
    if (rc) {
      nasd_printf("EDRFS: got 0x%x (%s) adding timeout for shutdown-quiesce\n",
        rc, nasd_error_string(rc));
    }
    NASD_WAIT_COND(nasd_edrfs_shutdown_cond,nasd_edrfs_rpc_cnt_lock);
  }
  NASD_UNLOCK_MUTEX(nasd_edrfs_rpc_cnt_lock);
}

void
nasd_edrfs_shutdown_rpc(
  void  *ignored)
{
#if DEBUG_RPC_SHUTDOWN > 0
  nasd_printf("nasd_edrfs_shutdown_rpc(): nasd_edrfs_threads_in_rpc=%ld\n",
    nasd_edrfs_threads_in_rpc);
#endif /* DEBUG_RPC_SHUTDOWN > 0 */
  nasd_edrfs_wait_shutdown_rpc();

#if DUMP_OP_STATS > 0
  DUMP_OPSTAT(null);
  DUMP_OPSTAT(mount);
  DUMP_OPSTAT(fsstat);
  DUMP_OPSTAT(fsinfo);
  DUMP_OPSTAT(lookup);
  DUMP_OPSTAT(readdir);
  DUMP_OPSTAT(access);
  DUMP_OPSTAT(setattr);
  DUMP_OPSTAT(create);
  DUMP_OPSTAT(remove);
  DUMP_OPSTAT(mkdir);
  DUMP_OPSTAT(rmdir);
  DUMP_OPSTAT(newcookie);
  DUMP_OPSTAT(rename);
  DUMP_OPSTAT(getstats);
#endif /* DUMP_OP_STATS > 0 */
#if NASD_EDRFS_DUMP_CACHE_STATS > 0
  NASD_EDRFS_CSDUMP(dir_lookup);
  NASD_EDRFS_CSDUMP(dir_force);
  NASD_EDRFS_CSDUMP(dirc_load_attr);
  NASD_EDRFS_CSDUMP(dirc_not_dir);
  NASD_EDRFS_CSDUMP(dirc_load_dir);
  NASD_EDRFS_CSDUMP(dirc_dirty);
  NASD_EDRFS_CSDUMP(dirc_new_dirty);
  NASD_EDRFS_CSDUMP(dirc_mkfree_loop);
  NASD_EDRFS_CSDUMP(dirc_mkfree_wait);
  NASD_EDRFS_CSDUMP(dir_load);
  NASD_EDRFS_CSDUMP(post_attribute);
  NASD_EDRFS_CSDUMP(post_attribute_valid);
  NASD_EDRFS_CSDUMP(post_attribute_invalid);
  NASD_EDRFS_CSDUMP(lookup_post_attribute);
  NASD_EDRFS_CSDUMP(dir_writer_fls);
  NASD_EDRFS_CSDUMP(dir_writer_pages);
  NASD_EDRFS_CSDUMP(dir_load_attr);
  NASD_EDRFS_CSDUMP(do_getattr);
  NASD_EDRFS_CSDUMP(do_setattr);
  NASD_EDRFS_CSDUMP(access_lookup_fail);
  NASD_EDRFS_CSDUMP(access_not_dir);
  NASD_EDRFS_CSDUMP(access_fault_attr);
  NASD_EDRFS_CSDUMP(access_get_attr);
  NASD_EDRFS_CSDUMP(lookup_get_attr);
  NASD_EDRFS_CSDUMP(setattr_get_attr);
#endif /* NASD_EDRFS_DUMP_CACHE_STATS > 0 */
}

nasd_status_t
nasd_edrfs_rpc_init()
{
  nasd_status_t rc;

  nasd_edrfs_rpc_qdepth = 0;

  bzero((char *)&nasd_edrfs_opstats, sizeof(nasd_edrfs_opstats));

  if (nasd_edrfs_threads_in_rpc) {
    /* already/still running */
    return(NASD_FAIL);
  }

  nasd_edrfs_wait_shutdown = 0;
  nasd_edrfs_threads_in_rpc = 0;

  rc = nasd_cond_init(&nasd_edrfs_shutdown_cond);
  if (rc)
    return(rc);
  rc = nasd_shutdown_cond(nasd_edrfs_shutdown, &nasd_edrfs_shutdown_cond);
  if (rc) {
    return(rc);
  }

  rc = nasd_mutex_init(&nasd_edrfs_rpc_cnt_lock);
  if (rc)
    return(rc);
  rc = nasd_shutdown_mutex(nasd_edrfs_shutdown, &nasd_edrfs_rpc_cnt_lock);
  if (rc) {
    return(rc);
  }

  rc = nasd_mutex_init(&nasd_edrfs_rpc_qdepth_lock);
  if (rc)
    return(rc);
  rc = nasd_shutdown_mutex(nasd_edrfs_shutdown, &nasd_edrfs_rpc_qdepth_lock);
  if (rc) {
    return(rc);
  }

  rc = nasd_edrfs_rpc_specific_init();
  if (rc) {
    return(rc);
  }

  return(NASD_SUCCESS);
}

nasd_status_t
nasd_edrfs_startup_rpc()
{
  nasd_status_t rc;

  rc = nasd_edrfs_rpc_specific_startup();
  if (rc)
    return(rc);

#ifndef KERNEL
  rc = nasd_edrfs_mq_specific_startup();
  if (rc)
    return rc;
#endif /* !KERNEL */

  rc = nasd_shutdown_proc(nasd_edrfs_shutdown, nasd_edrfs_shutdown_rpc, NULL);
  if (rc) {
    nasd_edrfs_shutdown_rpc(NULL);
    return(rc);
  }

  nasd_edrfs_active = 1;

  return(NASD_SUCCESS);
}

void
nasd_edrfs_stop_rpc()
{
  nasd_edrfs_rpc_specific_stop();
#ifndef KERNEL
  nasd_edrfs_mq_specific_stop();
#endif /* !KERNEL */
}

nasd_status_t
nasd_edrfs_rpc_listen(
  int          service_threads,
  nasd_uint16  ipport)
{
  nasd_status_t rc;

  rc = nasd_edrfs_rpc_specific_listen(service_threads, ipport);

  return(rc);
}

nasd_status_t
nasd_edrfs_rpc_set_stacksize(
  int  stacksize)
{
  nasd_status_t rc;

  rc = nasd_edrfs_rpc_specific_set_stacksize(stacksize);

  return(rc);
}

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