/*
 * nasd_od_ops.c
 *
 * RPC management for embedded NASD
 *
 * 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_drive_options.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_sys.h>
#include <nasd/nasd_cache.h>
#include <nasd/nasd_pdrive.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_marshall.h>
#include <nasd/nasd_keymgmt_dr.h>

#include <nasd/nasd_pipe.h>

#include <nasd/nasd_security.h>
#include <nasd/nasd_security_dr.h>

#include <nasd/nasd_timer.h>
#include <nasd/nasd_trace_dr.h>

#include <nasd/nasd_timeout.h>

#ifndef KERNEL
#include <nasd/nasd_udppipe.h>
#endif /* !KERNEL */

#ifdef NETBSD
#include <errno.h>
#endif


nasd_uint64 nasd_od_threads_in_rpc = 0;
nasd_uint64 nasd_od_shutdown_timeouts = 0;
extern nasd_uint64 nasd_od_shutdown_timeouts_max;
extern nasd_timespec_t nasd_od_shutdown_timeout_interval;
int nasd_od_wait_shutdown = 0;
nasd_timeout_handle_t nasd_od_shutdown_timeout_handle;

NASD_DECLARE_COND(nasd_od_shutdown_cond)
NASD_DECLARE_MUTEX(nasd_od_rpc_cnt_lock)
NASD_DECLARE_MUTEX(nasd_od_rpc_qdepth_lock)
NASD_DECLARE_MUTEX(nasd_od_rpc_minmax_lock)

int nasd_od_rpc_qdepth = 0;
nasd_timer_t nasd_od_rpc_qdepth_timer;

int nasd_pdrive_active = 0;
int nasd_od_rpc_cnt_lock_initted = 0;

extern char nasd_odc_zeroblk[NASD_OD_BASIC_BLOCKSIZE];
extern char nasd_odc_bitbucket[NASD_OD_BASIC_BLOCKSIZE];
nasd_ctrl_drive_opstats_t nasd_drive_opstats;

#ifndef KERNEL
int nasd_od_udp_drive_socket;
#endif /* !KERNEL */

#define DUMP_OPSTAT(_opname_) \
  nasd_drive_dump_opstat(&nasd_drive_opstats._opname_,NASD_STRING(_opname_))

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

#if (NASD_OD_RPC_DEBUG_RPC_SHUTDOWN > 0) || (NASD_DRIVE_SHUTDOWN_DEBUG > 0)
  nasd_gettime(&ts);
  nasd_printf("DRIVE: shutdown timeout at %s, nasd_od_threads_in_rpc=%"
    NASD_64u_FMT "\n", nasd_timestr_r(ts, str), nasd_od_threads_in_rpc);
#endif /* NASD_OD_RPC_DEBUG_RPC_SHUTDOWN > 0 || NASD_DRIVE_SHUTDOWN_DEBUG > 0 */
  if (nasd_od_threads_in_rpc == 0) {
    NASD_BROADCAST_COND(nasd_od_shutdown_cond);
  }
  nasd_od_shutdown_timeouts++;
}

/*
 * We have our own shutdown routine so we can keep
 * track of whether or not this is already
 * initialized.
 */
void
nasd_drive_rpc_shutdown_rpc_cnt_lock(
  void  *ignored)
{
  nasd_status_t rc;

  rc = nasd_mutex_destroy(&nasd_od_rpc_cnt_lock);
  if (rc) {
    nasd_printf("DRIVE ERROR: got 0x%x (%s) shutting down nasd_od_rpc_cnt_lock\n",
      rc, nasd_error_string(rc));
  }

  nasd_od_rpc_cnt_lock_initted = 0;
}

nasd_status_t
nasd_drive_rpc_init()
{
  nasd_status_t rc;

  nasd_od_rpc_qdepth = 0;

  bzero((char *)&nasd_drive_opstats, sizeof(nasd_drive_opstats));
  nasd_drive_opstats.ctrl_id = NASD_CTRL_DRIVE_INFO;

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

  nasd_od_wait_shutdown = 0;
  nasd_od_threads_in_rpc = 0;
  nasd_od_shutdown_timeouts = 0;

  if (nasd_od_rpc_cnt_lock_initted) {
    nasd_printf("DRIVE ERROR: nasd_od_rpc_cnt_lock_initted unexpectedly "
           "has value %d\n", nasd_od_rpc_cnt_lock_initted);
    return(NASD_FAIL);
  }

  rc = nasd_mutex_init(&nasd_od_rpc_cnt_lock);
  if (rc)
    return(rc);
  nasd_od_rpc_cnt_lock_initted = 1;
  rc = nasd_shutdown_proc(nasd_odc_shutdown,
    nasd_drive_rpc_shutdown_rpc_cnt_lock, NULL);
  if (rc) {
    nasd_drive_rpc_shutdown_rpc_cnt_lock(NULL);
    return(rc);
  }

  rc = nasd_cond_init(&nasd_od_shutdown_cond);
  if (rc)
    return(rc);
  rc = nasd_shutdown_cond(nasd_odc_shutdown, &nasd_od_shutdown_cond);
  if (rc) {
    return(rc);
  }

  rc = nasd_mutex_init(&nasd_od_rpc_qdepth_lock);
  if (rc)
    return(rc);
  rc = nasd_shutdown_mutex(nasd_odc_shutdown, &nasd_od_rpc_qdepth_lock);
  if (rc) {
    return(rc);
  }

  rc = nasd_mutex_init(&nasd_od_rpc_minmax_lock);
  if (rc)
    return(rc);
  rc = nasd_shutdown_mutex(nasd_odc_shutdown, &nasd_od_rpc_minmax_lock);
  if (rc) {
    return(rc);
  }

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

  return(NASD_SUCCESS);
}

void
nasd_drive_begin_rpc(
  nasd_opstat_t  *opstat)
{
  NASD_ATOMIC_INC64(&nasd_od_threads_in_rpc);
}

void
nasd_drive_end_rpc(
  nasd_opstat_t  *opstat)
{
  NASD_ATOMIC_DEC64(&nasd_od_threads_in_rpc);
  if (nasd_od_wait_shutdown && (nasd_od_threads_in_rpc == 0)) {
    NASD_BROADCAST_COND(nasd_od_shutdown_cond);
  }
}

void
nasd_drive_wait_shutdown_rpc()
{
  nasd_status_t rc;

  nasd_od_wait_shutdown = 1;
  rc = NASD_FAIL;

  if (nasd_od_rpc_cnt_lock_initted == 0)
    return;

  NASD_LOCK_MUTEX(nasd_od_rpc_cnt_lock);
  while(nasd_od_threads_in_rpc) {
    if (rc) {
      rc = nasd_timeout_add(&nasd_od_shutdown_timeout_handle,
        nasd_od_timeout_shutdown_rpc, NULL, NULL,
        nasd_od_shutdown_timeout_interval,
        nasd_od_shutdown_timeout_interval,
        NASD_TIMEOUT_F_PERIODIC);
    }
    if (rc) {
      nasd_printf("DRIVE: got 0x%x (%s) adding timeout for shutdown-quiesce\n",
        rc, nasd_error_string(rc));
    }
    NASD_WAIT_COND(nasd_od_shutdown_cond,nasd_od_rpc_cnt_lock);
  }
  NASD_UNLOCK_MUTEX(nasd_od_rpc_cnt_lock);
}

void
nasd_drive_stop_rpc()
{
  nasd_pdrive_active = 0;

  nasd_drive_rpc_specific_stop();

#ifndef KERNEL
  close(nasd_od_udp_drive_socket);
  nasd_od_udp_drive_socket = -1;
#endif /* !KERNEL */
}

void
nasd_drive_shutdown_rpc()
{
#if NASD_OD_RPC_DEBUG_RPC_SHUTDOWN > 0
  nasd_printf("nasd_shutdown_rpc(): nasd_od_threads_in_rpc=%" NASD_64u_FMT "\n",
    nasd_od_threads_in_rpc);
#endif /* NASD_OD_RPC_DEBUG_RPC_SHUTDOWN > 0 */

  nasd_drive_wait_shutdown_rpc();

#if NASD_OD_RPC_DUMP_OP_STATS > 0
  DUMP_OPSTAT(null);
  DUMP_OPSTAT(sync);
  DUMP_OPSTAT(part_creat);
  DUMP_OPSTAT(create);
  DUMP_OPSTAT(getattr);
  DUMP_OPSTAT(read_simple);
  DUMP_OPSTAT(tread_simple);
  DUMP_OPSTAT(write_simple);
  DUMP_OPSTAT(setattr);
  DUMP_OPSTAT(flush_obj);
  DUMP_OPSTAT(remove);
  DUMP_OPSTAT(initialize);
  DUMP_OPSTAT(change_key);
  DUMP_OPSTAT(remote_attach);
  DUMP_OPSTAT(remote_detach);
  DUMP_OPSTAT(remote_invoke);
#endif /* NASD_OD_RPC_DUMP_OP_STATS > 0 */

#if NASD_OD_RPC_DEBUG_RPC_SHUTDOWN > 0
  nasd_printf("done nasd_shutdown_rpc()\n");
#endif /* NASD_OD_RPC_DEBUG_RPC_SHUTDOWN > 0 */
}

nasd_status_t
nasd_drive_startup_rpc()
{
  nasd_status_t rc;
#ifndef KERNEL
  struct sockaddr_in saddr;
  int sockbuf;
#endif /* !KERNEL */

  rc = nasd_drive_rpc_specific_startup();
  if (rc) {
    nasd_printf("DRIVE ERROR: cannot startup RPC component, error 0x%x (%s)\n",
      rc, nasd_error_string(rc));
    return(rc);
  }

#ifndef KERNEL
  nasd_od_udp_drive_socket = socket(AF_INET, SOCK_DGRAM, 0);
  if(nasd_od_udp_drive_socket < 0) {
    nasd_printf("DRIVE ERROR: cannot create UDP drive socket errno=%d (%s)\n", errno,
           strerror(errno));
    return NASD_FAIL;
  }

  sockbuf = NASD_OD_UDP_SNDBUF;
  if(setsockopt(nasd_od_udp_drive_socket, SOL_SOCKET, SO_SNDBUF,
                (char *)&sockbuf, sizeof(int)) != 0) {
    nasd_printf("ERROR: cannot set SO_SNDBUF to %d: errno=%d (%s)\n",
           NASD_OD_UDP_SNDBUF, errno, strerror(errno));
    return NASD_FAIL;
  }

  bzero(&saddr, sizeof(struct sockaddr_in));
  saddr.sin_family = AF_INET;
  saddr.sin_port = 0;
  saddr.sin_addr.s_addr = INADDR_ANY;
  if(bind(nasd_od_udp_drive_socket, (struct sockaddr *) &saddr,
          sizeof(struct sockaddr_in)) != 0) {
    nasd_printf("ERROR: cannot bind UDP drive socket: errno=%d (%s)\n", errno,
           strerror(errno));
    return NASD_FAIL;
  }
#endif /* !KERNEL */

  nasd_pdrive_active = 1;

  return(NASD_SUCCESS);
}

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

  rc = nasd_drive_rpc_specific_listen(service_threads, ipport);

  return(rc);
}

void
nasd_drive_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);
}

nasd_status_t
nasd_drive_rpc_set_stacksize(
  int  stacksize)
{
  nasd_status_t rc;

  rc = nasd_drive_rpc_specific_set_stacksize(stacksize);

  return(rc);
}

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