/*
 * nasd_dux_sys.c
 *
 * In-kernel NASD system support
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,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_server.h>
#include <nasd/nasd_options.h>

#include <sys/secdefines.h>
#if SEC_BASE > 0
#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 <ufs/quota.h>
#include <ufs/inode.h>
#include <ufs/fs.h>
#include <ufs/ufsmount.h>
#include <ufs/fs_proto.h>
#include <ufs/ufs_stats.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 <kern/ipc_globals.h>
#include <sys/malloc.h>

#include <nasd/nasd_types.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_sys.h>
#include <nasd/nasd_security.h>

#include <mach/kern_return.h>

#define PRINT_ERROR 1


#define DOSYSRETURN(_val_) { \
  *retval = _val_; \
  return(_val_); \
}

nasd_svinfo_t      *nasd_sva = NULL;
nasd_edrfs_svinfo_t  *nasd_edrfs_sva = NULL;

extern kern_return_t vm_map_copy_overwrite(vm_map_t dest_map,
  vm_offset_t dest_addr, vm_map_copy_t copy, boolean_t interruptible);

extern vm_offset_t vm_alloc_kva(vm_size_t size);

int nasd_wire_err_verbose = 1;
int nasd_wire_tries = 5;
unsigned long nasd_wire_retries = 0;

NASD_DECLARE_ONCE(nasd_dux_kinit_once)

void
nasd_dux_kinit()
{
  nasd_status_t rc;

  rc = nasd_srv_init();
  if (rc) {
    dprintf("NASD: unable to init srv subsys\n");
  }
}

int
nasd_srv(p, args, retval)
  struct proc  *p;
  void         *args;
  long         *retval;
{
  struct args {
    int    cmd;
    void  *arg;
  } *uap = (struct args *)args;
  nasd_timeout_test_t timeout_test;
  struct nasd_edrfs_mount *edrfsmounts;
  struct nasd_edrfs_config edrfscfg;
  struct nasd_serv nasdsrv;
  nasd_options_t opts;
  nasd_status_t rc;
  int error;

  nasd_once(&nasd_dux_kinit_once, nasd_dux_kinit);

  switch(uap->cmd) {

    case NASD_SC_GET_OPTIONS:
      bzero((char *)&opts, sizeof(opts));
#if NASD_SECURE_RPCS_ENABLE > 0
      opts.opts1 |= NASD_OPTS1_SECURITY;
#endif /* NASD_SECURE_RPCS_ENABLE > 0 */
      opts.opts1 |= NASD_OPTS1_EDRFS_CLIENT;
      opts.opts1 |= NASD_OPTS1_EDRFS_FM;
      opts.opts1 |= NASD_OPTS1_DRIVE;
      opts.opts1 |= NASD_OPTS1_KPDEV;
      opts.opts_rpc = NASD_OPTS_RPC_COLOCATE;
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE
      opts.opts_rpc |= NASD_OPTS_RPC_DCE;
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_DCE */
#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_SRPC
      opts.opts_rpc |= NASD_OPTS_RPC_SRPC;
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_SRPC */
      opts.opts_seclevel = NASD_MAX_SECURITY_LEVEL;
      error = copyout((caddr_t)&opts, (caddr_t)uap->arg, sizeof(opts));
      DOSYSRETURN(error);
      break;

    case NASD_SC_TIMEOUT_TEST:
      error = copyin((caddr_t)uap->arg, (caddr_t)&timeout_test,
        sizeof(timeout_test));
      if (error) {
        DOSYSRETURN(error);
      }
#if NASD_TIMEOUT_TEST > 0
      rc = nasd_timeout_test(timeout_test.delta);
#else /* NASD_TIMEOUT_TEST > 0 */
      rc = NASD_OP_NOT_SUPPORTED;
#endif /* NASD_TIMEOUT_TEST > 0 */
      error = copyout((caddr_t)&timeout_test, (caddr_t)uap->arg,
        sizeof(timeout_test));
      DOSYSRETURN(error);
      break;

    case NASD_SC_PSRV_GO:
      PRIV_SUSER(u.u_cred, &u.u_acflag, SEC_REMOTE, 0, EPERM, error);
      if (error) {
        DOSYSRETURN(error);
      }

      NASD_D_LOCK();

      if (nasd_sva) {
        NASD_D_UNLOCK();
        DOSYSRETURN(EADDRINUSE);
      }
      error = copyin((caddr_t)uap->arg, (caddr_t)&nasdsrv, sizeof(nasdsrv));
      if (error) {
        NASD_D_UNLOCK();
        DOSYSRETURN(error);
      }
      rc = nasd_threads_init();
      if (rc) {
        NASD_D_UNLOCK();
        DOSYSRETURN(EIO);
      }
      rc = nasd_mem_init();
      if (rc) {
        nasd_threads_shutdown();
        NASD_D_UNLOCK();
        DOSYSRETURN(EIO);
      }
      NASD_Malloc(nasd_sva, sizeof(nasd_svinfo_t),
        (nasd_svinfo_t *));
      if (nasd_sva == NULL) {
        NASD_D_UNLOCK();
        nasd_mem_shutdown();
        nasd_threads_shutdown();
        DOSYSRETURN(ENOMEM);
      }
      bcopy((char *)&nasdsrv, (char *)&nasd_sva->srv,
        sizeof(nasd_svinfo_t));
      error = nasd_psrv_go(nasd_sva); /* consumes D_LOCK */
      /* nasd_mem_shutdown() done by nasd_psrv_go() */
      /* D_LOCK released by nasd_psrv_go() */
      DOSYSRETURN(error);
      break;
  
    case NASD_SC_SRV_PANIC:
      PRIV_SUSER(u.u_cred, &u.u_acflag, SEC_REMOTE, 0, EPERM, error);
      if (error) {
        DOSYSRETURN(error);
      }

      NASD_PANIC();
      break;

    case NASD_SC_EDRFS_SRV_GO:
      PRIV_SUSER(u.u_cred, &u.u_acflag, SEC_REMOTE, 0, EPERM, error);
      if (error) {
        DOSYSRETURN(error);
      }

      NASD_D_LOCK();

      if (nasd_edrfs_sva) {
        NASD_D_UNLOCK();
        DOSYSRETURN(EADDRINUSE);
      }
      error = copyin((caddr_t)uap->arg, (caddr_t)&edrfscfg, sizeof(edrfscfg));
      if (error) {
        NASD_D_UNLOCK();
        DOSYSRETURN(error);
      }
      if (edrfscfg.mount_array_len <= 0) {
        NASD_D_UNLOCK();
        DOSYSRETURN(EINVAL);
      }
      rc = nasd_threads_init();
      if (rc) {
        NASD_D_UNLOCK();
        DOSYSRETURN(EIO);
      }
      rc = nasd_mem_init();
      if (rc) {
        nasd_threads_shutdown();
        NASD_D_UNLOCK();
        DOSYSRETURN(EIO);
      }
      NASD_Malloc(nasd_edrfs_sva, sizeof(nasd_edrfs_svinfo_t),
        (nasd_edrfs_svinfo_t *));
      if (nasd_edrfs_sva == NULL) {
       NASD_D_UNLOCK();
       nasd_mem_shutdown();
       nasd_threads_shutdown();
       DOSYSRETURN(ENOMEM);
      }
      NASD_Malloc(edrfsmounts,
        sizeof(struct nasd_edrfs_mount)*edrfscfg.mount_array_len,
        (struct nasd_edrfs_mount *));
      if (edrfsmounts == NULL) {
        NASD_Free(nasd_edrfs_sva, sizeof(nasd_edrfs_svinfo_t));
        nasd_edrfs_sva = NULL;
        NASD_D_UNLOCK();
        nasd_mem_shutdown();
        nasd_threads_shutdown();
        DOSYSRETURN(ENOMEM);
      }
      bcopy((char *)&edrfscfg, (char *)&nasd_edrfs_sva->config,
        sizeof(struct nasd_edrfs_config));
      error = copyin((caddr_t)edrfscfg.mount_array, (caddr_t)edrfsmounts,
        sizeof(struct nasd_edrfs_mount)*edrfscfg.mount_array_len);
      if (error) {
        NASD_Free(edrfsmounts,
          sizeof(struct nasd_edrfs_mount)*edrfscfg.mount_array_len);
        NASD_Free(nasd_edrfs_sva, sizeof(nasd_edrfs_svinfo_t));
        nasd_edrfs_sva = NULL;
        NASD_D_UNLOCK();
        nasd_mem_shutdown();
        nasd_threads_shutdown();
        DOSYSRETURN(error);
      }
      nasd_edrfs_sva->config.mount_array = edrfsmounts;
      error = nasd_edrfs_srv_go(nasd_edrfs_sva); /* consumes D_LOCK */
      /*
       * nasd_edrfs_srv_go() consumes D_LOCK, frees the mount array
       * (edrfsmounts) and the svinfo structure (nasd_edrfs_sva).
       */
      nasd_mem_shutdown();
      nasd_threads_shutdown();
#if NASD_MEM_COUNT_ALLOC > 0
      if (nasd_mem_allocated) {
        printf(
          "EDRFS WARNING: %" NASD_MEMALLOC_FMT " bytes of memory still outstanding,"
          " something leaked core.\n",
          nasd_mem_allocated);
      }
      else {
        printf("EDRFS: 0 bytes of memory still outstanding.\n");
      }
#endif /* NASD_MEM_COUNT_ALLOC > 0 */
      DOSYSRETURN(error);
      break;

    case NASD_SC_ATOMIC_MATH_TEST:
      rc = nasd_threads_init();
      if (rc) {
        nasd_printf("NASD: could not initialize threads subsystem\n");
        DOSYSRETURN(EIO);
      }
      rc = nasd_atomic_test();
      nasd_threads_shutdown();
      if (rc) {
        DOSYSRETURN(EIO);
      }
      else {
        DOSYSRETURN(ESUCCESS);
      }
      break;

    default:
      NASD_D_UNLOCK();
      DOSYSRETURN(EINVAL);
  }

  DOSYSRETURN(0);
}

#if 0
#define NASD_WIRE_WRITE_MAP kernel_copy_map
#else
#define NASD_WIRE_WRITE_MAP ipc_kernel_map
#endif

int
nasd_sys_wire_buf(
  void   *buf,
  int     len,
  void  **iobufp,
  int     dir)
{
  void *m;
  int rc;

#if 0
  if (dir == NASD_WIRE_DIR_WRITE) {
    vm_map_copy_t vmcpy;
    kern_return_t ret;
    vm_offset_t kaddr;
    vm_map_t umap;
    task_t utask;

    utask = current_task();
    umap = utask->map;

    vm_map_reference(umap);

    vmcpy = NULL;
    ret = vm_map_copyin(umap, buf, len, FALSE, &vmcpy);
    vm_map_deallocate(umap); /* must do no matter success of above */
    if (ret != KERN_SUCCESS) {
      if (nasd_wire_err_verbose)
        dprintf("failed vm_map_copyin len=%d ret=%d\n", len, ret);
      return(EIO);
    }

    kaddr = 0;
    ret = vm_map_copyout(NASD_WIRE_WRITE_MAP, &kaddr, vmcpy);
    if (ret != KERN_SUCCESS) {
      if (nasd_wire_err_verbose)
        dprintf("failed vm_map_copyout len=%d vmcpy=0x%lx ret=%d\n", len, vmcpy, ret);
      vm_map_copy_discard(vmcpy);
      return(EIO);
    }

    *iobufp = (void *)kaddr;
    return(0);
  }
  else {
#endif
    vm_offset_t ustart, uend, ud;
    vm_offset_t kaddr, k1;
    vm_map_copy_t vmcpy;
    kern_return_t ret;
    vm_map_t umap;
    task_t utask;
    int ulen, try;

    utask = current_task();
    umap = utask->map;

    ustart = trunc_page((vm_offset_t)buf);
    uend = round_page(((vm_offset_t)buf) + len);
    ulen = uend - ustart;
    ud = ((vm_offset_t)buf) - ustart;

    vm_map_reference(umap);

    try = 0;

retry_wire:
    ret = vm_map_pageable(umap, ustart, uend, VM_PROT_READ|VM_PROT_WRITE);
    if (ret) {
      if (nasd_wire_err_verbose) {
        extern boolean_t vm_page_free_wanted;
        extern int ubc_pages, vm_page_free_reserved;
#if NTIP == 0
        extern int ubc_lru_page_count;
#endif /* NTIP == 0 */
        extern int vm_page_free_count, vm_page_zeroed_count, vm_managed_pages;
        extern int vm_page_kluster, vm_page_bad_count, vm_page_free_target;
        extern int vm_page_inactive_count;

        dprintf("failed vm_map_pageable ustart=0x%lx uend=0x%lx ret=%d\n", ustart, uend, ret);

        dprintf("ubc_pages=%d\n", ubc_pages);
        dprintf("nasd_wire_retries=%lu\n", nasd_wire_retries);
#if (NTIP==0)
        dprintf("ubc_lru_page_count=%d\n", ubc_lru_page_count);
#endif /* NTIP==0 */
        dprintf("vm_page_free_target=%d\n", vm_page_free_target);
        dprintf("vm_page_free_reserved=%d\n", vm_page_free_reserved);
        dprintf("vm_page_inactive_count=%d\n", vm_page_inactive_count);
        dprintf("vm_page_free_wanted=%d\n", vm_page_free_wanted);
        dprintf("vm_page_free_count=%d\n", vm_page_free_count);
        dprintf("vm_page_zeroed_count=%d\n", vm_page_zeroed_count);
        dprintf("vm_managed_pages=%d\n", vm_managed_pages);
        dprintf("vm_page_kluster=%d\n", vm_page_kluster);
        dprintf("vm_page_bad_count=%d\n", vm_page_bad_count);
      }
      try++;
      if ((try < nasd_wire_tries) && (ret == KERN_RESOURCE_SHORTAGE)) {
        vm_wait();
        nasd_wire_retries++;
        goto retry_wire;
      }
      vm_map_deallocate(umap);
      return(EIO);
    }

    ret = vm_dup_va(umap, ustart, ulen, &kaddr, VM_PROT_READ|VM_PROT_WRITE);
    if (ret) {
      if (nasd_wire_err_verbose) {
        dprintf("failed vm_dup_va umap=0x%lx ustart=0x%lx ulen=0x%lx ret=%d\n",
          umap, ustart, ulen, ret);
      }
      ret = vm_map_pageable(umap, ustart, uend, VM_PROT_NONE);
      if (ret) {
        dprintf("nasd_pdev.c line %d failed unwiring 0x%lx..0x%lx ret=%d\n", ustart, uend, ret);
      }
      return(EIO);
    }

    vm_map_deallocate(umap);

    *iobufp = (void *)(kaddr+ud);
    return(0);
#if 0
  }
#endif
}

int
nasd_sys_unwire_buf(
  void  *buf,
  int    in_len,
  void  *iobuf,
  int    dir)
{
#if 0
  if (dir == NASD_WIRE_DIR_WRITE) {
    vm_deallocate(NASD_WIRE_WRITE_MAP, iobuf, in_len);
    return(0);
  }
  else {
#endif
    vm_offset_t ustart, uend, ud;
    vm_offset_t start, end;
    vm_map_copy_t vmcpy;
    kern_return_t ret;
    vm_map_t umap;
    int len, ulen;
    task_t utask;

    utask = current_task();
    umap = utask->map;

    vm_map_reference(umap);

    ustart = trunc_page((vm_offset_t)buf);
    uend = round_page(((vm_offset_t)buf) + in_len);
    ulen = uend - ustart;
    ud = ((vm_offset_t)buf) - ustart;

    start = trunc_page((vm_offset_t)iobuf);
    end = round_page(((vm_offset_t)iobuf)+in_len);
    len = end - start;

    NASD_ASSERT(len == ulen);
    ret = vm_free_dup_va(start, ulen);
    if (ret) {
      dprintf("nasd_pdev.c line %d ret=%d from vm_free_dup_va 0x%lx len=%d\n",
        __LINE__, ret, start, ulen);
    }

    ret = vm_map_pageable(umap, ustart, uend, VM_PROT_NONE);
    if (ret) {
      dprintf("nasd_pdev.c line %d failed unwiring 0x%lx..0x%lx\n",
        __LINE__, ustart, uend);
    }

    vm_map_deallocate(umap);

    return(0);
#if 0
  }
#endif
}

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