#!/bin/sh
#

# --- GLOBALS --- #

OCT_CTRL=/opt/meru/bin/oct/oct-ctrl

# Count # of octeons in the system (returns numeric value)
if [ ! -f "$OCT_CTRL" ]; then
     # if there is no oct-ctrl function, we haven't installed meru-octeon package
     OCT_COUNT=0
else
     OCT_COUNT=`${OCT_CTRL} count`
fi

# Proc path for fastpath commands
FP_PROC=/proc/fastpath

# Proc path for Octeon commands
OCT_PROC=/proc/octeon/octeon

# Proc path for sysctl stats
SYSCTL_PATH=/proc/sys/fastpath/

# Default path for global Octeon commands
OCT_CMD=${OCT_PROC}0

# Which engine the user has flagged w/ the -e option
ENGINE_SEL=0

# Padding between engine stat columns in user-friendly mode
GRID_PADDING=14

# Default fastpath state = off
FASTPATH_DEFAULT=0

# Set to 1 to *not* display the cache column heading
NOHEADER=0

#Fastpath database info
START_DB=/opt/meru/etc/merudb.dbs
RUNNING_DB=/opt/meru/var/run/running-db/merudb.dbs
DBTABLE=MosWncVars
SEARCH_FIELD=m_FastPath

#Paths
SUBSQL_CMD=/opt/meru/bin/subsql
PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH

# --- ARRAYS --- #

# sysctl file entries that are global (singular)                                               V this column is to display on/off instead of number
SYSCTL_GLOBAL_SIZE=3
SYSCTL_GLOBAL=(          fastpath_on         "fastpath_on"       "Fastpath status:          "  1
                         hardware_accel      "hardware_accel"    "Hardware acceleration:    "  1
                         num_engines         "num_engines"       "Number of accel engines:  "  0  )


# Per engine stats                                                                                   V this column means HW-only
SYSCTL_PER_ENGINE_SIZE=20
SYSCTL_PER_ENGINE=(      cache_width            "cache_width"           "Cache width:              "        0
                         cache_depth            "cache_depth"           "Cache depth:              "        0
                         cache_adds             "cache_adds"            "Cache adds:               "        0
                         cache_deletes          "cache_deletes"         "Cache deletes:            "        0
                         cache_null_deletes     "cache_null_deletes"    "Cache null deletes:       "        0
                         cache_flushes          "cache_flushes"         "Cache flushes:            "        0
                         cache_updates          "cache_updates"         "Cache updates:            "        0
                         cache_replaces         "cache_replaces"        "Cache replaces:           "        0
                         cache_active_entries   "cache_active_entries"  "Cache active entries:     "        0
                         cache_collisions       "cache_collisions"      "Cache collisions:         "        0
                         cache_add_throttles    "cache_add_throttles"   "Cache add throttles:      "        0
                         total_packets          "total_packets"         "Total packets:            "        0
                         eligible_packets       "eligible_packets"      "Eligible packets:         "        0
                         downstream_trials      "downstream_trials"     "Downstream trials:        "        0
                         downstream_hits        "downstream_hits"       "Downstream hits:          "        0
                         upstream_trials        "upstream_trials"       "Upstream trials:          "        0
                         upstream_hits          "upstream_hits"         "Upstream hits:            "        0
                         hits_first_try         "hits_first_try"        "Hits, 1st try:            "        0
                         hits_second_try        "hits_second_try"       "Hits, 2nd try:            "        0
                         hits_nth_try           "hits_nth_try"          "Hits, Nth try:            "        0
                         engine_load            "engine_load"           "Engine Load:              "        1    )




# --- FUNCTIONS --- #

# distributes command in "$1" to all Octeon proc entries
distrib_command()
{
  for ((i = 0; i < OCT_COUNT; i++))
  do
    echo $1 > ${OCT_PROC}${i}
  done
}

# Enable fastpath
# 1. turns on software fastpath
# If Octeon cards exist: 
#   2. installs fastpath hooks for the Octeon cards
#   3. turns on the fastpath cache on the Octeon cards
fastpath_enable()
{
     echo "enable" > $FP_PROC

     if [ $OCT_COUNT -ne 0 ]; then
          # Install hooks for the Octeon cards (only needs to be called on one entry)
          echo "install_hooks" > $OCT_CMD
          # Enable fastpath on the cards
          distrib_command "enable_fastpath=1" 
     fi
}

# Disable fastpath
# 1. turns off software fastpath
# If Octeon cards exist:
#   2. Remove fastpath hooks for the Octeon cards
#   3. turn off the fastpath cache on the Octeon cards
fastpath_disable()
{
     echo "disable" > $FP_PROC

     if [ $OCT_COUNT -ne 0 ]; then
          # Remove hooks for the Octeon cards (only needs to be called on one entry)
          echo "remove_hooks" > $OCT_CMD
          # Disable fastpath on the cards
          distrib_command "enable_fastpath=0" 
     fi
}

# displays arg $1 with a globally-specified space padding afterwards
disp_padded_value()
{
     arglen=${#1}
     paddinglen=`expr $GRID_PADDING - $arglen`
     echo -n $1
     for ((i = 0; i < paddinglen; i++))
     do
          echo -n " "
     done
}

# Displays user-friendly stats (from ioscli, bash, etc).  For human consumption.
fastpath_stats()
{
     hwaccel=`cat ${SYSCTL_PATH}hardware_accel`
     echo ""

     # Print global stats
     next_stat=0
     for ((ind = 0; ind < SYSCTL_GLOBAL_SIZE; ind++))
     do
          stat_proc=${SYSCTL_GLOBAL[${next_stat}]}
          next_stat=`expr $next_stat + 2`
          stat_desc=${SYSCTL_GLOBAL[${next_stat}]}
          next_stat=`expr $next_stat + 1`
          stat_disp=${SYSCTL_GLOBAL[${next_stat}]}
          next_stat=`expr $next_stat + 1`

          stat_value=`cat ${SYSCTL_PATH}${stat_proc}`

          if [ "$stat_disp" -eq 1 ]; then
               if [ $stat_value -eq 1 ]; then
                    stat_value="On"
               else
                    stat_value="Off"
               fi
          fi

          echo "${stat_desc}${stat_value}"
     done

     echo ""

     # Print per-engine stats

     # Start with the engine index display (for HW stats only)
     if [ $hwaccel -eq 1 ]; then
          num_engines=`cat ${SYSCTL_PATH}num_engines`
          echo -n "Engine index:             "
          for ((ind = 0; ind < num_engines; ind++))
          do
               disp_padded_value $ind
          done
          echo ""
     fi

     # and then print all stats
     next_stat=0
     for ((ind = 0; ind < SYSCTL_PER_ENGINE_SIZE; ind++))
     do
          stat_proc=${SYSCTL_PER_ENGINE[${next_stat}]}
          next_stat=`expr $next_stat + 2`
          stat_desc=${SYSCTL_PER_ENGINE[${next_stat}]}
          next_stat=`expr $next_stat + 1`
          stat_hw=${SYSCTL_PER_ENGINE[${next_stat}]}
          next_stat=`expr $next_stat + 1`

          # Display the stat if we have HW accel or the stat is also for SW
          if [ $hwaccel -eq 1 -o $stat_hw -eq 0 ]; then
               stat_value=`cat ${SYSCTL_PATH}${stat_proc}`
               echo -n "${stat_desc}"
               for stat in $stat_value; do
                   disp_padded_value $stat
               done
               echo ""
          fi
     done
}

# Display stats in metrics form
fastpath_metrics()
{
     hwaccel=`cat ${SYSCTL_PATH}hardware_accel`

     # Print global stats
     next_stat=0
     for ((ind = 0; ind < SYSCTL_GLOBAL_SIZE; ind++))
     do
          stat_proc=${SYSCTL_GLOBAL[${next_stat}]}
          next_stat=`expr $next_stat + 1`
          stat_desc=${SYSCTL_GLOBAL[${next_stat}]}
          next_stat=`expr $next_stat + 2`
          stat_disp=${SYSCTL_GLOBAL[${next_stat}]}
          next_stat=`expr $next_stat + 1`

          stat_value=`cat ${SYSCTL_PATH}${stat_proc}`

          echo "${stat_desc} ${stat_value}"
     done

     # Print per-engine stats
     next_stat=0
     for ((ind = 0; ind < SYSCTL_PER_ENGINE_SIZE; ind++))
     do
          stat_proc=${SYSCTL_PER_ENGINE[${next_stat}]}
          next_stat=`expr $next_stat + 1`
          stat_desc=${SYSCTL_PER_ENGINE[${next_stat}]}
          next_stat=`expr $next_stat + 2`
          stat_hw=${SYSCTL_PER_ENGINE[${next_stat}]}
          next_stat=`expr $next_stat + 1`

          # Display the stat if we have HW accel or the stat is also for SW
          if [ $hwaccel -eq 1 -o $stat_hw -eq 0 ]; then
               stat_value=`cat ${SYSCTL_PATH}${stat_proc}`
               echo -n "${stat_desc} "
               for stat in $stat_value; do
                   echo -n "$stat "
               done
               echo ""
          fi
     done
}

# Display the proc entry for the software fastpath (or selected hardware engine: -e command)
fastpath_procshow()
{
     if [ $OCT_COUNT -eq 0 ]; then
          cat $FP_PROC
     else
          cat ${OCT_PROC}${ENGINE_SEL}
     fi
}

# Reset fastpath stats
fastpath_reset_stats()
{
     echo "reset_stats" > $FP_PROC
     distrib_command "reset_stats"
}

fastpath_debug_level()
{
     if [ -f ${SYSCTL_PATH}debug_level ]; then
          echo $1 > ${SYSCTL_PATH}debug_level
     else
          echo "That feature is not supported on this platform."
     fi
}

fastpath_detail_level()
{
     if [ -f ${SYSCTL_PATH}detail_level ]; then
          echo $1 > ${SYSCTL_PATH}detail_level
     else
          echo "That feature is not supported on this platform."
     fi
}

# Flush fastpath caches
fastpath_flush()
{
     echo "flush" > $FP_PROC
}

# Pull fastpath setting from the controller DB
fastpath_get_dbstatus()
{
     #Check our DB files.  Use the running DB first, then startup, then just use default
     if [ -f $RUNNING_DB ]; then
          DBFILE=$RUNNING_DB
     elif [ -f $START_DB ]; then
          DBFILE=$START_DB
     else
          FASTPATH_CONF=$FASTPATH_DEFAULT
          return
     fi

     SEARCH_TEXT=`echo "open '$DBFILE'; select * from $DBTABLE;" | $SUBSQL_CMD`

     FIELD_LIST=`echo "$SEARCH_TEXT" | grep $SEARCH_FIELD | sed "s/>> //"`
     VALUE_LIST=`echo "$SEARCH_TEXT" | grep -A 1 $SEARCH_FIELD | sed "/^>>/d"`

     #Get the index of the SEARCH_FIELD
     field_index=0
     field_name=$SEARCH_FIELD
     field_len=${#field_name}
     field_list=$FIELD_LIST
     find_failed=0
     emergency_break=100

     #Iterate through the table fields to find our match
     while true; do

          #End of list, or emergency break
          if [ ${#field_list} -eq 0 -o $emergency_break -eq $field_index ]; then
               find_failed=1
               break
          fi

          #We found it!
          if [ "${field_list:0:$field_len}" = "$field_name" ]; then
               break
          fi

          #Go to the next field and increment index
          next_space=`expr index "$field_list" " "`
          field_list=${field_list:$next_space}
          field_index=`expr $field_index + 1`

     done

     #If we failed to the field, set to default
     if [ $find_failed -eq 1 ]; then
          FASTPATH_CONF=$FASTPATH_DEFAULT
          return
     fi

     #Clean up the value list so that its easily parsable
     value_list=`echo "$VALUE_LIST" | sed "s/(//" | sed "s/)//" | sed "s/'[^']*'/s/g" | sed "s/-[^,]*/n/g" | sed "s/,//g"`

     #For each index that we pass, strip out the value
     cur_index=0
     while [ $cur_index -ne $field_index ]; do
          value_list=`echo "$value_list" | sed "s/[^ ]* //"`
          cur_index=`expr $cur_index + 1`
     done

     #Check if we're the last value or not
     sp_ind=`expr index "$value_list" " "`

     #If we're the last value, just return.  Otherwise we have to extract our value from the beginning of the list
     if [ $sp_ind -eq 0 ]; then
          final_val=$value_list
     else
          sp_ind=`expr $sp_ind - 1`
          final_val=${value_list:0:$sp_ind}
     fi

     #final_val is our value!
     FASTPATH_CONF=$final_val
}

# Restore system fastpath state to the DB setting
# Useful for external scripts that do not know the current state
fastpath_restore_to_db()
{
     fastpath_get_dbstatus
     if [ $FASTPATH_CONF -eq 1 ]; then
          fastpath_enable
     else
          fastpath_disable
     fi
}

# Display cache entries for engine ENGINE_SEL
fastpath_dumpcache()
{
     # Show all entries? (even empty)
     SHOWALL=0
	 SKIPEMPTY="+"
     if [ "$1" = "all" ]; then
          SHOWALL=1
		  SKIPEMPTY=
     fi

     # Get cache width/depth
     cache_width=`cat ${SYSCTL_PATH}cache_width | awk '{ print $1 }'`
     cache_depth=`cat ${SYSCTL_PATH}cache_depth | awk '{ print $1 }'`

     if [ $NOHEADER -eq 0 ]; then
          echo ""
          echo "Upstream Cache Entries:"
          echo ""
          echo "Buckt E D    Source IP     Port  Destination IP  Port PR    Station MAC       Upstream MAC   VLAN  Age "
          echo "----- - - --------------- ----- --------------- ----- -- ----------------- ----------------- ---- ------"
     fi

     # Dump upstream
     for ((width = 0; width < cache_width; width++))
     do
          for ((depth = 0; depth < cache_depth; depth++))
          do
               # Ask for the specific bucket slot
               echo "$width $depth u${SKIPEMPTY} $ENGINE_SEL" > ${SYSCTL_PATH}cache_view
               # And get its value
               cache_slot=`cat ${SYSCTL_PATH}cache_view`
          
               if [ $SHOWALL -eq 1 ]; then
                    echo "$cache_slot"
               else
                    if [ "$cache_slot" = "-" ]; then
                         # We're done - break out of the loop
                         depth=$cache_depth
                         width=$cache_width
                    else
                         # Move to the next entry
                         width=`echo $cache_slot | awk '{ print $1 }'`
                         depth=`echo $cache_slot | awk '{ print $2 }'`
                         echo "$cache_slot"
                    fi
               fi
          done
     done     
     
     if [ $NOHEADER -eq 0 ]; then
          echo ""
          echo "Downstream Cache Entries:"
          echo ""
          echo "Buckt E D    Source IP     Port  Destination IP  Port PR    Station MAC      Downstream MAC        AP IP      Port  Age "
          echo "----- - - --------------- ----- --------------- ----- -- ----------------- ----------------- --------------- ----- ------"
     fi

     # Dump downstream
     for ((width = 0; width < cache_width; width++))
     do
          for ((depth = 0; depth < cache_depth; depth++))
          do
               # Ask for the specific bucket slot
               echo "$width $depth d${SKIPEMPTY} $ENGINE_SEL" > ${SYSCTL_PATH}cache_view
               # And get its value
               cache_slot=`cat ${SYSCTL_PATH}cache_view`

               if [ $SHOWALL -eq 1 ]; then
                    echo "$cache_slot"
               else
                    if [ "$cache_slot" = "-" ]; then
                         # We're done - break out of the loop
                         depth=$cache_depth
                         width=$cache_width
                    else
                         # Move to the next entry
                         width=`echo $cache_slot | awk '{ print $1 }'`
                         depth=`echo $cache_slot | awk '{ print $2 }'`
                         echo "$cache_slot"
                    fi
               fi
          done
     done     
}

fastpath_dumpindex()
{
     hwaccel=`cat ${SYSCTL_PATH}hardware_accel`
     if [ $hwaccel -eq 1 ]; then
          echo "dumpindex not supported with hardware fastpath."
          return
     fi

     echo ""
     echo "Cache MAC Index Entries:"
     echo ""
     echo "Buckt       MAC              References "
     echo "----- ----------------- ---------------------------------------------"

     # Dump upstream
     for ((width = 0; width < 8192; width++))
     do
       echo "$width" > ${SYSCTL_PATH}index_view
          
       index_slot=`cat ${SYSCTL_PATH}index_view`

       echo "$index_slot"

       if [ `echo $index_slot | awk '{ print $1 }'` = "-" ]; then
              # We're done - break out of the loop
           width=8192
       else
           # Move to the next entry
           width=`echo $index_slot | awk '{ print $1 }'`
       fi
     done     
}

fastpath_dumpcache_sw()
{
     echo "dump" > $FP_PROC
     dmesg
}

# Make sure our environment supports Octeon scripts
ioscli_checksupport()
{
     if [ ! -f "${OCT_CTRL}" ]; then
          echo "This platform is not configured to support hardware acceleration."
          exit 1
     fi
}

ioscli_checkengine()
{
     if [ $OCT_COUNT -eq 0 ]; then
          echo "There are no hardware acceleration interfaces on this machine."
          exit 1
     fi 

     if [ ! $ENGINE_SEL -lt $OCT_COUNT ]; then
          if [ $OCT_COUNT -eq 1 ]; then
               echo "Invalid interface.  There is only 1 interface on this platform."               
          else
               echo "Invalid interface.  There are only $OCT_COUNT interfaces on this platform."
          fi
          exit 1
     fi

     if [ ! -f "${OCT_PROC}${ENGINE_SEL}" ]; then
          echo "There is a driver misconfiguration."
          echo "The acceleration interface does not have a kernel entry."
          echo "Please contact support."
          exit 1
     fi
}

# Display ioscli printout of number of engines
ioscli_showaccel()
{
     ioscli_checksupport
     
     if [ $OCT_COUNT -eq 1 ]; then
          echo "1 hardware acceleration interface card detected."
     else
          echo "$OCT_COUNT hardware acceleration interface cards detected."
     fi
}

# Display one-time output of card ENGINE_SEL
ioscli_showstate()
{
     ioscli_checksupport
     ioscli_checkengine
     cat ${OCT_PROC}${ENGINE_SEL}
}

# Use octeon_monitor to display continuous output of ENGINE_SEL
ioscli_showstatereal()
{
     ioscli_checksupport
     ioscli_checkengine
     /opt/meru/bin/oct/meru/octeon_monitor -o $ENGINE_SEL
}

# Display command usage
fastpath_usage()
{
     echo "Meru Networks Fastpath Configuration Tool"
     echo ""
     echo "Usage:"
     echo ""
     echo "fastpath -h"
     echo "fastpath -?"
     echo "fastpath --help                         Display this usage info."
     echo ""
     echo "fastpath enable                         Turn on the fastpath subsystem"
     echo ""
     echo "fastpath disable                        Turn off the fastpath subsystem."
     echo ""
     echo "fastpath restore                        Restore fastpath to DB state."
     echo ""
     echo "fastpath flush                          Flush the fastpath cache."
     echo ""
     echo "fastpath stats                          Display fastpath stats in human-"
     echo "                                        readable format."
     echo ""
     echo "fastpath metrics                        Display fastpath stats in parsable"
     echo "                                        format."
     echo ""
     echo "fastpath buckets                        Display bucket stat and info in dmesg."
     echo ""
     echo "fastpath reset_stats                    Reset fastpath stats."
     echo ""
     echo "fastpath width <width>                  Set cache width (causes fastpath reset)."
     echo ""
     echo "fastpath depth <depth>                  Set cache depth (causes fastpath reset)."
     echo ""
     echo "fastpath debug_level <level>            Set the debug level."
     echo ""
     echo "fastpath datail_level <level>           Set the detail level for debug items."
     echo ""
     echo "fastpath [-e <engine>] procshow         Show fastpath proc entry."
     echo "                                        (Use -e to specify hardware engine)."
     echo "                                        Defaults to the first engine."
     echo ""
     echo "fastpath [-e <engine>] dumpcache        Dump fastpath cache (active entries)."
     echo ""
     echo "fastpath [-e <engine>] dumpcache all    Dump fastpath cache (all entries)."
     echo ""
     echo "fastpath showaccel                      (ioscli) Displays number of accel"
     echo "                                        engines for the IOSCLI command."
     echo ""
     echo "fastpath [-e <engine>] showstate        (ioscli) Displays the accel engine"
     echo "                                        state for the IOSCLI (proc echo)."
     echo ""
     echo "fastpath [-e <engine>] showstatereal    (ioscli) Displays the accel engine"
     echo "                                        state in real time for IOSCLI."
     echo ""
}

# ----------------------- COMMAND LINE PARSING --------------------------- #

if [ $# -eq 0 ]; then
    fastpath_stats
    exit 1
fi

#Deal with command line parameters
while [ $# -ge 1 ]; do
case "$1" in
     "-h" | "-?" | "--help" )
          fastpath_usage
          exit 1
          ;;

     "-n" )
          NOHEADER=1
          ;;

     "-e" )
          shift
          ENGINE_SEL=$1
          ;;

     "enable" )
          fastpath_enable
          exit 0
          ;;

     "disable" )
          fastpath_disable
          exit 0
          ;;

     "restore" )
          fastpath_restore_to_db
          exit 0
          ;;

     "procshow" )
          fastpath_procshow
          exit 0
          ;;

     "flush" )
          fastpath_flush
          exit 0
          ;;

     "reset_stats" )
          fastpath_reset_stats
          exit 0
          ;;

     "width" )
          fastpath_disable
          echo $2 > ${SYSCTL_PATH}/cache_width
          echo "create" > $FP_PROC
          fastpath_restore_to_db
          exit 0
          ;;

     "depth" )
          fastpath_disable
          echo $2 > ${SYSCTL_PATH}/cache_depth
          echo "create" > $FP_PROC
          fastpath_restore_to_db
          exit 0
          ;;

     "debug_level" )
          fastpath_debug_level $2
          exit 0
          ;;

     "detail_level" )
          fastpath_detail_level $2
          exit 0
          ;;

     "stats" )
          fastpath_stats
          exit 0
          ;;

     "metrics" )
          fastpath_metrics
          exit 0
          ;;

     "dumpcache" )
          fastpath_dumpcache $2
          exit 0
          ;;

     "dumpindex" )
          fastpath_dumpindex
          exit 0
          ;;

     "dump" )
          fastpath_dumpcache_sw
          exit 0
          ;;

     "buckets" )
          cat ${SYSCTL_PATH}/bucket_view
          exit 0
          ;;

     "showaccel" )
          ioscli_showaccel
          exit 0
          ;;

     "showstate" )
          ioscli_showstate
          exit 0
          ;;

     "showstatereal" )
          ioscli_showstatereal
          exit 0
          ;;

     *)
          fastpath_usage
          exit 1
          ;;
esac
shift
done                       
