/*********************************************************
 * Copyright (C) 2009 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation version 2 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 *********************************************************/

/*
 * stats.h --
 *
 *      Stats functions for Linux vsock module.
 */

#ifndef __STATS_H__
#define __STATS_H__

#include "driver-config.h"

#include "vm_basic_math.h"

#include "vsockCommon.h"
#include "vsockPacket.h"


/*
 * Define VSOCK_GATHER_STATISTICS to turn on statistics gathering.
 * Currently this consists of 2 types of stats:
 * 1. The number of control datagram messages sent.
 * 2. The level of queuepair fullness (in 10% buckets) whenever data is
 *    about to be enqueued or dequeued from the queuepair.
 */

//#define VSOCK_GATHER_STATISTICS 1

#ifdef VSOCK_GATHER_STATISTICS

#define VSOCK_NUM_QUEUE_LEVEL_BUCKETS 10
extern uint64 vSockStatsCtlPktCount[VSOCK_PACKET_TYPE_MAX];
extern uint64 vSockStatsConsumeQueueHist[VSOCK_NUM_QUEUE_LEVEL_BUCKETS];
extern uint64 vSockStatsProduceQueueHist[VSOCK_NUM_QUEUE_LEVEL_BUCKETS];

#define VSOCK_STATS_STREAM_CONSUME_HIST(vsk)                            \
   VSockVmciStatsUpdateQueueBucketCount((vsk)->consumeQ,                \
                                        (vsk)->produceQ,                \
                                        (vsk)->consumeSize,             \
                                        vSockStatsConsumeQueueHist)
#define VSOCK_STATS_STREAM_PRODUCE_HIST(vsk)                            \
   VSockVmciStatsUpdateQueueBucketCount((vsk)->produceQ,                \
                                        (vsk)->consumeQ,                \
                                        (vsk)->produceSize,             \
                                        vSockStatsProduceQueueHist)
#define VSOCK_STATS_CTLPKT_LOG(pktType)                                 \
do {                                                                    \
   ++vSockStatsCtlPktCount[pktType];                                    \
} while (0)
#define VSOCK_STATS_CTLPKT_DUMP_ALL()                                   \
   VSockVmciStatsCtlPktDumpAll()
#define VSOCK_STATS_HIST_DUMP_ALL()                                     \
   VSockVmciStatsHistDumpAll()
#define VSOCK_STATS_RESET() VSockVmciStatsReset

/*
 *----------------------------------------------------------------------------
 *
 * VSockVmciStatsUpdateQueueBucketCount --
 *
 *      Given a queue, determine how much data is enqueued and add that to
 *      the specified queue level statistic bucket.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
VSockVmciStatsUpdateQueueBucketCount(VMCIQueue *mainQueue,  // IN
                                     VMCIQueue *otherQueue, // IN
                                     uint64 mainQueueSize,  // IN
                                     uint64 queueHist[])    // IN
{
   uint64 bucket = 0;
   uint32 remainder = 0;
   uint64 dataReady = VMCIQueue_BufReady(mainQueue,
                                         otherQueue,
                                         mainQueueSize);
   /*
    * We can't do 64 / 64 = 64 bit divides on linux because it requires a libgcc
    * which is not linked into the kernel module. Since this code is only used by
    * developers we just limit the mainQueueSize to be less than MAX_UINT for now.
v    */
   ASSERT(mainQueueSize <= MAX_UINT32);
   Div643264(dataReady * 10, mainQueueSize, &bucket, &remainder);
   ASSERT(bucket < VSOCK_NUM_QUEUE_LEVEL_BUCKETS);
   ++queueHist[bucket];
}


/*
 *----------------------------------------------------------------------------
 *
 * VSockVmciStatsCtlPktDumpAll --
 *
 *      Prints all stream control packet counts out to the console using
 *      the appropriate platform logging.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
VSockVmciStatsCtlPktDumpAll(void)
{
   uint32 index;

   ASSERT_ON_COMPILE(VSOCK_PACKET_TYPE_MAX ==
		     ARRAYSIZE(vSockStatsCtlPktCount));

   for (index = 0; index < ARRAYSIZE(vSockStatsCtlPktCount); index++) {
      Warning("Control packet count: Type = %u, Count = %"FMT64"u\n",
              index, vSockStatsCtlPktCount[index]);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * VSockVmciStatsHistDumpAll --
 *
 *      Prints the produce and consume queue histograms to the console.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
VSockVmciStatsHistDumpAll(void)
{
   uint32 index;

   #define VSOCK_DUMP_HIST(strname, name) do {             \
      for (index = 0; index < ARRAYSIZE(name); index++) {  \
         Warning(strname " Bucket count %u = %"FMT64"u\n", \
              index, name[index]);                         \
      }                                                    \
   } while (0)

   VSOCK_DUMP_HIST("Produce Queue", vSockStatsProduceQueueHist);
   VSOCK_DUMP_HIST("Consume Queue", vSockStatsConsumeQueueHist);

   #undef VSOCK_DUMP_HIST
}


/*
 *----------------------------------------------------------------------------
 *
 * VSockVmciStatsReset --
 *
 *      Reset all VSock statistics.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
VSockVmciStatsReset(void)
{
   uint32 index;

   #define VSOCK_RESET_ARRAY(name) do {                   \
      for (index = 0; index < ARRAYSIZE(name); index++) { \
         name[index] = 0;                                 \
      }                                                   \
   } while (0)

   VSOCK_RESET_ARRAY(vSockStatsCtlPktCount);
   VSOCK_RESET_ARRAY(vSockStatsProduceQueueHist);
   VSOCK_RESET_ARRAY(vSockStatsConsumeQueueHist);

   #undef VSOCK_RESET_ARRAY
}

#else
#define VSOCK_STATS_STREAM_CONSUME_HIST(vsk)
#define VSOCK_STATS_STREAM_PRODUCE_HIST(vsk)
#define VSOCK_STATS_CTLPKT_LOG(pktType)
#define VSOCK_STATS_CTLPKT_DUMP_ALL()
#define VSOCK_STATS_HIST_DUMP_ALL()
#define VSOCK_STATS_RESET()
#endif // VSOCK_GATHER_STATISTICS

#endif // __STATS_H__
