/*
 * nasd_od_rpc.h
 *
 * Assist macros and definitions for NASD drive
 * RPC subsystem
 *
 * 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.
 */


#ifndef _NASD_OD_RPC_H_
#define _NASD_OD_RPC_H_

#include <nasd/nasd_options.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_control.h>
#include <nasd/nasd_od.h>

#include <nasd/nasd_base_types_marshall.h>
#include <nasd/nasd_control_marshall.h>
#include <nasd/nasd_drive_types_marshall.h>
#include <nasd/nasd_error_marshall.h>
#include <nasd/nasd_pdrive_marshall.h>
#include <nasd/nasd_types_marshall.h>

/*
 * minmax only works when keeping stats
 */
#define NASD_OD_RPC_KEEP_OP_STATS         1
#define NASD_OD_RPC_DUMP_OP_STATS         0
#define NASD_OD_RPC_OP_QUEUE_TIMES        0
#define NASD_OD_RPC_OP_MINMAX             1

#define NASD_OD_RPC_DEBUG_RPC_START_STOP  0
#define NASD_OD_RPC_DEBUG_RPC_RETURNS     0
#define NASD_OD_RPC_DEBUG_RPC_ERR_RETURNS 0
#define NASD_OD_RPC_DEBUG_RPC_SHUTDOWN    0
#define NASD_OD_RPC_DEBUG_DCE_EXCEPTION   1

extern nasd_uint64 nasd_od_threads_in_rpc;
extern int nasd_od_wait_shutdown;
NASD_DECLARE_EXTERN_COND(nasd_od_shutdown_cond)
NASD_DECLARE_EXTERN_MUTEX(nasd_od_rpc_cnt_lock)
NASD_DECLARE_EXTERN_MUTEX(nasd_od_rpc_qdepth_lock)
NASD_DECLARE_EXTERN_MUTEX(nasd_od_rpc_minmax_lock)

extern int nasd_pdrive_protseq_installed;
extern int nasd_pdrive_active;

extern int nasd_od_rpc_qdepth;
extern nasd_timer_t nasd_od_rpc_qdepth_timer;

extern char nasd_odc_zeroblk[NASD_OD_BASIC_BLOCKSIZE];
extern char nasd_odc_bitbucket[NASD_OD_BASIC_BLOCKSIZE];
extern nasd_freelist_t *nasd_ebuff_freelist;
extern nasd_ctrl_drive_opstats_t nasd_drive_opstats;

#if NASD_OD_RPC_OP_MINMAX > 0

#define NASD_OD_RPC_UPDATE_MINMAX(_opstat_,_nsecs_) { \
  NASD_LOCK_MUTEX(nasd_od_rpc_minmax_lock); \
  if ((_nsecs_) > (_opstat_)->max_nsecs) \
    (_opstat_)->max_nsecs = (_nsecs_); \
  if (((_nsecs_) < (_opstat_)->min_nsecs) || ((_opstat_)->min_nsecs == 0)) \
    (_opstat_)->min_nsecs = (_nsecs_); \
  NASD_UNLOCK_MUTEX(nasd_od_rpc_minmax_lock); \
}

#else /* NASD_OD_RPC_OP_MINMAX > 0 */

#define NASD_OD_RPC_UPDATE_MINMAX(_opstat_,_nsecs_) { /* noop */ }

#endif /* NASD_OD_RPC_OP_MINMAX > 0 */

#if NASD_OD_RPC_KEEP_OP_STATS > 0

#define NASD_OD_RPC_OP_STATS_DECL \
  nasd_timer_t _op_timer; \
  nasd_uint64 _el;

#define NASD_OD_RPC_OP_BEGIN(_opname_) { \
  NASD_ATOMIC_INC64(&nasd_drive_opstats._opname_.in_flight); \
  NASD_TM_START(&_op_timer); \
}

#define NASD_OD_RPC_OP_END(_opname_) { \
  NASD_TM_STOP(&_op_timer); \
  NASD_TM_ELAPSED_NSEC(&_op_timer,&_el); \
  NASD_ATOMIC_INC64(&nasd_drive_opstats._opname_.invalid); \
  NASD_ATOMIC_INC64(&nasd_drive_opstats._opname_.num_ops); \
  NASD_ATOMIC_DEC64(&nasd_drive_opstats._opname_.in_flight); \
  NASD_ATOMIC_ADD64(&nasd_drive_opstats._opname_.op_nsecs,_el); \
  NASD_ATOMIC_DEC64(&nasd_drive_opstats._opname_.invalid); \
  NASD_OD_RPC_UPDATE_MINMAX(&nasd_drive_opstats._opname_, _el); \
}

#else /* NASD_OD_RPC_KEEP_OP_STATS > 0 */

#define NASD_OD_RPC_OP_BEGIN(_opname_)
#define NASD_OD_RPC_OP_END(_opname_)
#define NASD_OD_RPC_OP_STATS_DECL \
  nasd_uint64 _el=0;

#endif /* NASD_OD_RPC_KEEP_OP_STATS > 0 */

#if NASD_OD_RPC_OP_QUEUE_TIMES > 0

#define NASD_OD_RPC_QBEGIN(_opname_) { \
  nasd_timespec_t _ts; \
  int _qd; \
  NASD_LOCK_MUTEX(nasd_od_rpc_qdepth_lock); \
  if (nasd_od_rpc_qdepth) { \
    NASD_TM_STOP(&nasd_od_rpc_qdepth_timer); \
    NASD_TM_ELAPSED_TS(&nasd_od_rpc_qdepth_timer, &_ts); \
  } \
  NASD_TM_START(&nasd_od_rpc_qdepth_timer); \
  if (nasd_od_rpc_qdepth) { \
    _qd = NASD_MIN(nasd_od_rpc_qdepth, NASD_CTRL_DRIVE_OPDEPTHS); \
    NASD_ATOMIC_TIMESPEC_ADD(&nasd_drive_opstats.opdepths[_qd-1], &_ts); \
  } \
  nasd_od_rpc_qdepth++; \
  NASD_UNLOCK_MUTEX(nasd_od_rpc_qdepth_lock); \
}

#define NASD_OD_RPC_QEND(_opname_) { \
  nasd_timespec_t _ts; \
  int _qd; \
  NASD_LOCK_MUTEX(nasd_od_rpc_qdepth_lock); \
  NASD_ASSERT(nasd_od_rpc_qdepth > 0); \
  NASD_TM_STOP(&nasd_od_rpc_qdepth_timer); \
  NASD_TM_ELAPSED_TS(&nasd_od_rpc_qdepth_timer, &_ts); \
  nasd_od_rpc_qdepth--; \
  if (nasd_od_rpc_qdepth) { \
    NASD_TM_START(&nasd_od_rpc_qdepth_timer); \
  } \
  _qd = NASD_MIN(nasd_od_rpc_qdepth, NASD_CTRL_DRIVE_OPDEPTHS-1); \
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_drive_opstats.opdepths[_qd], &_ts); \
  NASD_UNLOCK_MUTEX(nasd_od_rpc_qdepth_lock); \
}

#else /* NASD_OD_RPC_OP_QUEUE_TIMES > 0 */

#define NASD_OD_RPC_QBEGIN(_opname_) { /* noop */ }
#define NASD_OD_RPC_QEND(_opname_)   { /* noop */ }

#endif /* NASD_OD_RPC_OP_QUEUE_TIMES > 0 */

#if NASD_OD_RPC_DEBUG_RPC_START_STOP > 0
#define NASD_OD_RPC_SS_DBG_PRINTF(_a_) nasd_printf _a_
#else /* NASD_OD_RPC_DEBUG_RPC_START_STOP > 0 */
#define NASD_OD_RPC_SS_DBG_PRINTF(_a_)
#endif /* NASD_OD_RPC_DEBUG_RPC_START_STOP > 0 */

#if NASD_OD_RPC_DEBUG_RPC_RETURNS > 0
#define NASD_OD_RPC_RET_DBG_PRINTF(_a_) nasd_printf _a_
#else /* NASD_OD_RPC_DEBUG_RPC_RETURNS > 0 */
#if NASD_OD_RPC_DEBUG_RPC_ERR_RETURNS > 0
#define NASD_OD_RPC_RET_DBG_PRINTF(_a_) if (NASD_OD_RPC_NASD_RET) { nasd_printf _a_; }
#else /* NASD_OD_RPC_DEBUG_RPC_ERR_RETURNS > 0 */
#define NASD_OD_RPC_RET_DBG_PRINTF(_a_)
#endif /* NASD_OD_RPC_DEBUG_RPC_ERR_RETURNS > 0 */
#endif /* NASD_OD_RPC_DEBUG_RPC_RETURNS > 0 */


#define NASD_OD_RPC_DOBEGIN(_opname_,_file_) { \
  if (nasd_pdrive_active == 0) { \
    /* \
     * XXX should never happen, but DCE RPC \
     * is not as good at "shutdown" as we \
     * want. Ideally, this would set status \
     * equal to 0, and a useful error code in \
     * the nasd status field, but that would \
     * involve marshalling a reply here, which \
     * is more than we want to do. \
     * \
     * Might also happen for colocation. \
     */ \
    NASD_OD_RPC_RETURN(999); \
  } \
  NASD_OD_RPC_QBEGIN(_opname_); \
  nasd_drive_begin_rpc(&nasd_drive_opstats._opname_); \
  NASD_OD_RPC_OP_BEGIN(_opname_); \
  NASD_OD_RPC_SS_DBG_PRINTF(("Begin %s (file %s line %d) thread %" \
    NASD_THREAD_ID_FMT "\n", \
    NASD_STRING(_opname_), _file_, __LINE__, nasd_thread_self())); \
}

#define NASD_OD_RPC_DORETURN(_opname_,_file_) { \
  NASD_OD_RPC_SS_DBG_PRINTF(("End %s (file %s line %d)\n", \
    NASD_STRING(_opname_), _file_, __LINE__)); \
  NASD_OD_RPC_RET_DBG_PRINTF(("%s (file %s line %d) returning %d (%s)\n", \
    NASD_STRING(_opname_), _file_, __LINE__, NASD_OD_RPC_NASD_RET, \
    nasd_error_string(NASD_OD_RPC_NASD_RET))); \
  NASD_OD_RPC_OP_END(_opname_); \
  nasd_drive_end_rpc(&nasd_drive_opstats._opname_); \
  NASD_OD_RPC_QEND(_opname_); \
  NASD_OD_RPC_RETURN(0); \
}

typedef struct nasd_od_rpc_basic_s {
  nasd_status_t  nasd_status;
} nasd_od_rpc_basic_t;

#endif /* !_NASD_OD_RPC_H_ */

