/*
 * nasd_edrfs_kmain.c
 *
 * in-dux-kernel statup and support 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>

#ifdef DEC_OSF
#include <nasd_server.h>
#include <sys/secdefines.h>
#if SEC_BASE
#include <sys/security.h>
#include <sys/audit.h>
#include <sys/secpolicy.h>
#endif /* SEC_BASE */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/specdev.h>
#include <sys/flock.h>
#include <sys/syslimits.h>
#include <kern/assert.h>
#include <kern/parallel.h>
#include <mach/mach_types.h>
#include <vm/vm_page.h>
#include <vm/vm_vppage.h>
#include <sys/vfs_ubc.h>
#include <vm/vm_mmap.h>
#include <vm/vm_vp.h>
#include <vm/vm_debug.h>
#include <sys/malloc.h>
#include <machine/rpb.h>
#endif /* DEC_OSF */

#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_common.h>
#include <nasd/nasd_edrfs_server_internal.h>

extern nasd_edrfs_svinfo_t *nasd_edrfs_sva;

#ifdef LINUX
struct wait_queue *nasd_edrfs_linux_wq;
extern void nasd_linux_edrfs_fm_module_start(void);
extern void nasd_linux_edrfs_fm_module_stop(void);
#endif /* LINUX */

#ifdef DEC_OSF
#define nasd_status_to_errno(_s_)	nasd_dux_nasd_status_to_errno(_s_)
#endif /* DEC_OSF */
#ifdef LINUX
#define nasd_status_to_errno(_s_)	nasd_linux_nasd_status_to_errno(_s_)
#endif /* LINUX */

nasd_status_t
nasd_edrfs_frame_init()
{
  /* no kernel-specific stuff to init, here */
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_edrfs_do_drive_bind(
  char                 *bind_name,
  nasd_drive_handle_t  *dhp)
{
  nasd_status_t rc;

  if (nasd_edrfs_sva->config.colocate) {
    rc = nasd_bind_to_drive(bind_name, NASD_PDRIVE_PORT,
      NASD_BIND_COLOCATE, NULL, 0, dhp);
  }
  else {
    rc = nasd_bind_to_drive(bind_name, NASD_PDRIVE_PORT,
      NASD_BIND_DEFAULT, NULL, 0, dhp);
  }
  return(rc);
}

#ifdef DEC_OSF
#define STOP_PSRV_THREAD() { \
  thread_terminate(current_thread()); \
  thread_halt_self(); \
}
#endif /* DEC_OSF */

#ifdef LINUX
#define STOP_PSRV_THREAD() { return; }
#endif /* LINUX */

void
nasd_edrfs_srv_thread(
  nasd_threadarg_t  arg)
{
  nasd_edrfs_svinfo_t *svinfo;
  struct nasd_edrfs_config *edrfscfg;
  nasd_status_t rc;

  svinfo = (nasd_edrfs_svinfo_t *)arg;
  edrfscfg = &svinfo->config;

  if (edrfscfg->verbose) {
    nasd_printf("EDRFS: setup RPC\n");
  }

  rc = nasd_edrfs_startup_rpc();
  if (rc) {
    nasd_printf("EDRFS: got 0x%x (%s) from nasd_edrfs_startup_rpc()\n",
      rc, nasd_error_string(rc));
    STOP_PSRV_THREAD();
  }

  if (edrfscfg->verbose) {
    nasd_printf("EDRFS: begin listening\n");
  }

  if (edrfscfg->stack_size) {
    rc = nasd_edrfs_rpc_set_stacksize(edrfscfg->stack_size);
    if (rc == NASD_OP_NOT_SUPPORTED) {
      nasd_printf("EDRFS: setting stack size not supported\n");
    }
    else if (rc) {
      nasd_printf("EDRFS: got 0x%x (%s) setting stack size to %d\n",
        rc, nasd_error_string(rc), edrfscfg->stack_size);
    }
    else {
      nasd_printf("EDRFS: stack size set to %d\n", edrfscfg->stack_size);
    }
  }

  rc = nasd_edrfs_rpc_listen(edrfscfg->svc_threads, edrfscfg->ipport);
  if (rc) {
    nasd_printf("EDRFS: got 0x%x (%s) starting RPC listener\n",
      rc, nasd_error_string(rc));
  }

  STOP_PSRV_THREAD();
}

/*
 * Call with NASD_D_LOCK held, consumes the lock.
 */
int
nasd_edrfs_srv_go(
  nasd_edrfs_svinfo_t  *svinfo)
{
  nasd_thread_t nasd_k_handler_thread;
  struct nasd_edrfs_config *edrfscfg;
  struct nasd_edrfs_mount *edrfsmnt;
  int verbose, abort, error, i;
  nasd_status_t rc;

  edrfscfg = &svinfo->config;
  verbose = edrfscfg->verbose;
  abort = 1;

#if DEC_OSF
  /* GC unused pages */
  pmap_collect(vm_map_pmap(current_thread()->task->map));
#endif

#if LINUX
  nasd_linux_edrfs_fm_module_start();
  init_waitqueue(&nasd_edrfs_linux_wq);
#endif /* LINUX */

  rc = nasd_edrfs_init();
  if (rc) {
    nasd_printf("EDRFS: got 0x%x (%s) from nasd_edrfs_init()\n",
      rc, nasd_error_string(rc));
    error = nasd_status_to_errno(rc);
    goto done;
  }

  /* deal with all the mounts given us by the user */
  for(i=0;i<edrfscfg->mount_array_len;i++) {
    edrfsmnt = &edrfscfg->mount_array[i];
    rc = nasd_edrfs_add_mount(edrfsmnt->path, edrfsmnt->name,
      edrfsmnt->drive_ip_addr, edrfsmnt->portnum, edrfsmnt->partnum,
      edrfsmnt->net_addr);
    if (rc) {
      nasd_printf("EDRFS: got 0x%x (%s) adding mount for %s@%s\n",
        rc, nasd_error_string(rc),
        edrfsmnt->path, edrfsmnt->name);
      rc = nasd_edrfs_really_shutdown();
      if (rc) {
        nasd_printf("EDRFS: nasd_edrfs_really_shutdown() returned 0x%x (%s)!\n",
          rc, nasd_error_string(rc));
        NASD_PANIC();
      }
      error = nasd_status_to_errno(rc);
      goto done;
    }
  }

  rc = nasd_thread_create(&nasd_k_handler_thread,
    (void (*)())nasd_edrfs_srv_thread, (void *)svinfo);
  if (rc) {
    nasd_printf("nasd_edrfs_srv_go(): cannot create handler thread, rc=0x%x (%s)\n",
      rc, nasd_error_string(rc));
    error = nasd_status_to_errno(rc);
    goto done;
  }

  /* wait for kill */
#ifdef DEC_OSF
  assert_wait(0, TRUE);
#endif /* DEC_OSF */
  NASD_D_UNLOCK();

  if (verbose) {
    nasd_printf("EDRFS: thread away\n");
  }

#ifdef DEC_OSF
  thread_block();
#endif /* DEC_OSF */
#ifdef LINUX
  interruptible_sleep_on(&nasd_edrfs_linux_wq);
#endif /* LINUX */

  if (verbose) {
    nasd_printf("EDRFS: begin shutdown\n");
  }

  /* cleanup */
  NASD_D_LOCK();

  if (verbose) {
    nasd_printf("EDRFS: stop RPC subsystem\n");
  }

  nasd_edrfs_stop_rpc();

  if (verbose) {
    nasd_printf("EDRFS: sequence shutdown\n");
  }

  rc = nasd_edrfs_really_shutdown();
  if (rc) {
    nasd_printf("EDRFS: nasd_edrfs_really_shutdown() returned 0x%x (%s)!\n",
      rc, nasd_error_string(rc));
    NASD_PANIC();
  }

  abort = 0;
  error = EINTR;

done:

#ifdef free
#define xxx_xxxs_goats free
#undef free
#endif /* free */
  NASD_Free(edrfscfg->mount_array,
    sizeof(struct nasd_edrfs_mount)*edrfscfg->mount_array_len);
  NASD_Free(svinfo, sizeof(nasd_edrfs_svinfo_t));
#ifdef xxx_xxxs_goats
#define free xxx_xxxs_goats
#undef xxx_xxxs_goats
#endif /* xxx_xxxs_goats */

  nasd_edrfs_sva = NULL;

  NASD_D_UNLOCK();

#ifdef LINUX
  nasd_linux_edrfs_fm_module_stop();
#endif /* LINUX */

  if (verbose && (abort == 0)) {
    nasd_printf("EDRFS: done shutdown\n");
  }

  return(error);
}
