blob: 25d2263504fc4abc93c33b7456415dc8e7fc64bb [file] [log] [blame]
/*
* (C) Copyright 2003
* Author : Laurent Brando (Mindspeed Technologies)
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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; either version 2 of
* the License, or (at your option) any 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/**
* @file
* @brief Retrieve PFE Statistics
*/
//#define SBL_EMUL /* for compilation and debug on Linux Station */
#ifndef SBL_EMUL
#include <common.h>
#include <command.h>
#include <environment.h>
#include <driver.h>
#include <net.h>
#include <fs.h>
#include <errno.h>
#include <libbb.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include "../drivers/net/comcerto/pfe/pfe.h"
#else
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#define simple_strtoul(a,b,c) strtoul(a,b,c)
enum {
CLASS0_ID = 0,
CLASS1_ID,
CLASS2_ID,
CLASS3_ID,
CLASS4_ID,
CLASS5_ID,
TMU0_ID,
TMU1_ID,
TMU2_ID,
TMU3_ID,
UTIL_ID,
MAX_PE
};
#endif
typedef struct pfe_stat_queue_response {
u_int32_t emitted_pkts;
u_int32_t dropped_pkts;
u_int32_t peak_queue_occ;
} pfe_stat_queue_response_t;
typedef struct pfe_stat_interface_pkt_response {
u_int32_t total_bytes_received[2]; /* 64 bit counter stored as 2*32 bit counters */
u_int32_t total_bytes_transmitted[2]; /* 64 bit counter stored as 2*32 bit counters */
u_int32_t total_pkts_received;
u_int32_t total_pkts_transmitted;
} pfe_stat_interface_pkt_response_t;
#define NUM_QUEUES 8
#define NUM_INTERFACES 2
/* used to store either dmem base address of statistics counters structure or
statistics control register address (for enable/disable stats). Specified as command line argument */
unsigned int class_dmem_addr = 0;
unsigned int tmu_dmem_addr = 0;
/* Host copy of the statistics memory layout in PFE classifier */
typedef struct _tFppStatistics {
u_int64_t FS_total_bytes_received[NUM_INTERFACES];
u_int64_t FS_total_bytes_transmitted[NUM_INTERFACES];
u_int32_t FS_total_pkts_received[NUM_INTERFACES];
u_int32_t FS_total_pkts_transmitted[NUM_INTERFACES];
u_int64_t FS_emitted_pkts[NUM_INTERFACES][NUM_QUEUES];
u_int64_t FS_dropped_pkts[NUM_INTERFACES][NUM_QUEUES];
u_int32_t FS_peak_queue_occ[NUM_INTERFACES][NUM_QUEUES];
} tFppStatistics;
struct _tFppStatistics host_stats;
/* redefined here, because declared as static in /lib/vsprintf.c */
#define is_digit(c) ((c) >= '0' && (c) <= '9')
static int skip_atoi(char **s)
{
int i=0;
while (is_digit(**s))
i = i*10 + *((*s)++) - '0';
return i;
}
/* compute address of a given statistics counters whithin the PE's memory */
static unsigned int get_target_counter_address(unsigned int target_base, unsigned int counter)
{
//printf("%s:\n", __FUNCTION__);
return (target_base + (counter - (unsigned int)&host_stats));
}
/* retrieves the base address of the statistics structure within the PE memory */
static unsigned int get_target_dmem_address(unsigned char pe_id)
{
//printf("%s:\n", __FUNCTION__);
return (pe_id <= CLASS5_ID)? class_dmem_addr: tmu_dmem_addr;
}
/* Definitions of Bit Masks for the features */
#define STAT_QUEUE_BITMASK 0x00000001
#define STAT_INTERFACE_BITMASK 0x00000002
#ifndef caseretstr
#define caseretstr(x) case x: return #x
#endif
char *stats_mask_to_string(unsigned int mask)
{
switch (mask)
{
caseretstr(STAT_QUEUE_BITMASK);
caseretstr(STAT_INTERFACE_BITMASK);
default:
return "unknown\n";
}
}
unsigned int stats_string_to_mask(char * name)
{
if(strcmp(name, "queue") == 0)
return STAT_QUEUE_BITMASK;
if(strcmp(name, "interface") == 0)
return STAT_INTERFACE_BITMASK;
return 0;
}
#ifdef SBL_EMUL
unsigned int dummy_dmem[128] = {0};
static unsigned int pe_dmem_read(int id, unsigned int addr, unsigned char size)
{
printf("%s: read pe %i - @%08x val = %x - %d bytes\n", __FUNCTION__, id, addr, dummy_dmem[0], size);
return dummy_dmem[0];
}
static void pe_dmem_write(int id, unsigned int addr, unsigned int val, unsigned char size)
{
dummy_dmem[0] = val;
printf("%s: write pe %i - @%08x val = %x - %d bytes\n", __FUNCTION__, id, addr, val, size);
}
#define COMMAND_ERROR_USAGE (-1)
#else
extern unsigned int pe_dmem_read(int id, unsigned int addr, unsigned char size);
extern void pe_dmem_write(int id, unsigned int addr, unsigned int val, unsigned char size);
#endif
static void class_aggregate_stats(unsigned int *host_addr, unsigned int dmem_addr, unsigned char nbytes)
{
int pe_id;
unsigned char nwords;
unsigned int *dst;
unsigned int *src;
//printf("%s:\n", __FUNCTION__);
for(pe_id = CLASS0_ID; pe_id <= CLASS5_ID; pe_id++)
{
nwords = nbytes;
dst = host_addr;
src = (unsigned int *)dmem_addr;
while(nwords--)
{
*dst += pe_dmem_read(pe_id, (unsigned int)src, 4);
dst++;
src++;
}
}
}
static void tmu_aggregate_stats(unsigned int *host_addr, unsigned int dmem_addr, unsigned char nbytes, unsigned char interface)
{
int pe_id;
unsigned char nwords;
unsigned int *dst;
unsigned int *src;
//printf("%s:\n", __FUNCTION__);
if(interface == 0)
pe_id = TMU0_ID;
else if(interface == 1)
pe_id = TMU1_ID;
else
pe_id = TMU2_ID;
nwords = nbytes;
dst = host_addr;
src = (unsigned int *)dmem_addr;
while(nwords--)
{
*dst += pe_dmem_read(pe_id, (unsigned int)src, 4);
dst++;
src++;
}
}
static void pe_enable_stats(unsigned int mask, unsigned char enable)
{
int pe_id;
unsigned int stat_ctrl_addr;
unsigned int stat_ctrl;
printf("%s: %s %s\n", __FUNCTION__, (enable)? "enable":"disable",stats_mask_to_string(mask));
for(pe_id = CLASS0_ID; pe_id <= TMU2_ID; pe_id++)
{
stat_ctrl_addr = get_target_dmem_address(pe_id);
stat_ctrl = pe_dmem_read(pe_id, stat_ctrl_addr, 4);
if(enable)
stat_ctrl |= mask;
else
stat_ctrl &= ~mask;
pe_dmem_write(pe_id, stat_ctrl_addr, stat_ctrl, 4);
}
}
#ifndef SBL_EMUL
static int pfe_stats(struct command *cmdtp, int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
pfe_stat_queue_response_t queue_stats = {0};
pfe_stat_interface_pkt_response_t interface_pkt_stats = {{0}};
unsigned int counter_address, interface, queue;
if(argc < 4)
return COMMAND_ERROR_USAGE;
class_dmem_addr = simple_strtoul(argv[1], NULL, 0);
tmu_dmem_addr = simple_strtoul(argv[2], NULL, 0);
if(strcmp(argv[3], "enable") == 0)
{
if(argc != 5)
return COMMAND_ERROR_USAGE;
pe_enable_stats(stats_string_to_mask(argv[4]), 1);
}
else if(strcmp(argv[3], "disable") == 0)
{
if(argc != 5)
return COMMAND_ERROR_USAGE;
pe_enable_stats(stats_string_to_mask(argv[4]), 0);
}
else if(strcmp(argv[3], "query") == 0)
{
if(strcmp(argv[4], "queue") == 0)
{
if(argc != 8)
return COMMAND_ERROR_USAGE;
queue = simple_strtoul(argv[5], NULL, 0);
interface = simple_strtoul(argv[7], NULL, 0);
printf("QUEUE STATS: tmu dmem base 0x%x queue %d interface %d\n", tmu_dmem_addr, queue, interface);
if((queue > (NUM_QUEUES - 1)) || (interface > (NUM_INTERFACES - 1)))
return COMMAND_ERROR_USAGE;
/* get corresponding dmem address */
counter_address = get_target_counter_address(tmu_dmem_addr, (unsigned int)&host_stats.FS_emitted_pkts[interface][queue]);
/* copy and aggregate all queue_stats counters for the selected interface */
tmu_aggregate_stats((unsigned int *)&queue_stats, counter_address, sizeof(pfe_stat_queue_response_t)/4, interface);
printf("Emitted Pkts: %u \n"
"Dropped Packets: %u\n"
"Peak Queue Occupancy: %u \n",
queue_stats.emitted_pkts, queue_stats.dropped_pkts, queue_stats.peak_queue_occ);
}
else if(strcmp(argv[4], "interface") == 0)
{
if(argc != 6)
return COMMAND_ERROR_USAGE;
interface = simple_strtoul(argv[5], NULL, 0);
printf("INTERFACE STATS: class dmem base 0x%x tmu dmem base 0x%x interface %d\n", class_dmem_addr, tmu_dmem_addr, interface);
if(interface > (NUM_INTERFACES - 1))
return COMMAND_ERROR_USAGE;
/* get CLASSIFIER statistics for received counters */
counter_address = get_target_counter_address(class_dmem_addr, (unsigned int)&host_stats.FS_total_bytes_received[interface]);
class_aggregate_stats((unsigned int *)&interface_pkt_stats.total_bytes_received[0], counter_address, sizeof(u_int64_t));
counter_address = get_target_counter_address(class_dmem_addr, (unsigned int)&host_stats.FS_total_pkts_received[interface]);
class_aggregate_stats((unsigned int *)&interface_pkt_stats.total_pkts_received, counter_address, sizeof(u_int32_t));
/* get TMU statistics for transmit counters */
counter_address = get_target_counter_address(tmu_dmem_addr, (unsigned int)&host_stats.FS_total_bytes_transmitted[interface]);
tmu_aggregate_stats((unsigned int *)&interface_pkt_stats.total_bytes_transmitted[0], counter_address, sizeof(u_int64_t), interface);
counter_address = get_target_counter_address(tmu_dmem_addr, (unsigned int)&host_stats.FS_total_pkts_transmitted[interface]);
tmu_aggregate_stats((unsigned int *)&interface_pkt_stats.total_pkts_transmitted, counter_address, sizeof(u_int32_t), interface);
printf("Total Bytes Received: %llu \n"
"Total Bytes Transmitted: %llu \n"
"Total Packets Received: %u \n"
"Total Packets Transmitted: %u \n",
(unsigned long long)(interface_pkt_stats.total_bytes_received[0] | (interface_pkt_stats.total_bytes_received[1] << 31)),
(unsigned long long)(interface_pkt_stats.total_bytes_transmitted[0] | (interface_pkt_stats.total_bytes_transmitted[1] << 31)),
interface_pkt_stats.total_pkts_received, interface_pkt_stats.total_pkts_transmitted);
}
else
{
printf("statistics type %s not supported\n", argv[1]);
}
}
else
{
return COMMAND_ERROR_USAGE;
}
return 0;
}
#ifndef SBL_EMUL
static const __maybe_unused char cmd_pfe_stats_help[] =
"Usage:\n \
Enable/Disable Statistics: pfe_stat <class_gFpStatFeatureBitMask> <tmu_gFpStatFeatureBitMask> [enable | disable] [queue | interface]\n \
Query Queue Statistics: pfe_stat <class_gFpStatistics> <tmu_gFpStatistics> query queue <queue number> <interface number>\n \
Query Interface Statistics: pfe_stat <class_gFpStatistics> <tmu_gFpStatistics> query interface <interface number> ";
BAREBOX_CMD_START(pfe_stats)
.cmd = pfe_stats,
.usage = "retrieve PFE statistics",
BAREBOX_CMD_HELP(cmd_pfe_stats_help)
BAREBOX_CMD_END
#endif