blob: ddcdcf80698bfc9e6958df28f128042e75473abd [file] [log] [blame]
/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <getopt.h>
#include <stdint.h>
#include "pktlog_ac_fmt.h"
int pktlog_enable(char *sysctl_name, unsigned filter)
{
FILE *fp;
fp = fopen(sysctl_name, "w");
printf("Open status of the pktlog_enable:%p %s\n", fp, sysctl_name);
if (fp != NULL) {
fprintf(fp, "%i", filter);
fclose(fp);
return 0;
}
return -1;
}
int
pktlog_options(char *sysctl_options, unsigned long options)
{
FILE *fp;
fp = fopen(sysctl_options, "w");
if (fp == NULL)
return -1;
fprintf(fp, "%lu", options);
fclose(fp);
return 0;
}
int pktlog_size(char *sysctl_size, char *sysctl_enable, int size)
{
FILE *fp;
/* Make sure logging is disabled before changing size */
fp = fopen(sysctl_enable, "w");
if (fp == NULL) {
fprintf(stderr, "Open failed on enable sysctl\n");
return -1;
}
fprintf(fp, "%i", 0);
fclose(fp);
fp = fopen(sysctl_size, "w");
if (fp != NULL) {
fprintf(fp, "%i", size);
fclose(fp);
return 0;
}
return -1;
}
void usage()
{
fprintf(stderr,
"Packet log configuration\n"
"usage: pktlogconf [-a adapter] [-e[event-list]] [-d] [-s log-size] [-t -k -l]\n"
" [-b -p -i] \n"
" -h show this usage\n"
" -a configures packet logging for specific 'adapter';\n"
" configures system-wide logging if this option is\n"
" not specified\n"
" -d disable packet logging\n"
" -e enable logging events listed in the 'event-list'\n"
" event-list is an optional comma separated list of one or more\n"
" of the following: rx tx rcf rcu ani (eg., pktlogconf -erx,rcu,tx)\n"
" -s change the size of log-buffer to \"log-size\" bytes\n"
" -t enable logging of TCP headers\n"
" -k enable triggered stop by a threshold number of TCP SACK packets\n"
" -l change the number of packets to log after triggered stop\n"
" -b enable triggered stop by a throuput threshold\n"
" -p enable triggered stop by a PER threshold\n"
// not implemented
// " -y enable triggered stop by a threshold number of Phyerrs\n"
" -i change the time period of counting throughput/PER\n"
);
exit(-1);
}
int main(int argc, char *argv[])
{
int c;
int size = -1, tail_length = -1, sack_thr = -1;
int thruput_thresh = -1, per_thresh = -1, phyerr_thresh = -1, trigger_interval = -1;
unsigned long filter = 0, options = 0;
char fstr[24];
char ad_name[24];
char sysctl_size[128];
char sysctl_enable[128];
char sysctl_options[128];
char sysctl_sack_thr[128];
char sysctl_tail_length[128];
char sysctl_thruput_thresh[128];
char sysctl_phyerr_thresh[128];
char sysctl_per_thresh[128];
char sysctl_trigger_interval[128];
int opt_a=0, opt_d = 0, opt_e = 0, fflag=0;
for (;;) {
c = getopt(argc, argv, "s:e::a:d:tk:l:b:p:i:");
if (c < 0)
break;
switch (c) {
case 't':
options |= ATH_PKTLOG_PROTO;
break;
case 'k': /* triggered stop after # of TCP SACK packets are seen */
options |= ATH_PKTLOG_TRIGGER_SACK;
sack_thr = atoi(optarg);
break;
case 'l': /* # of tail packets to log after triggered stop */
tail_length = atoi(optarg);
break;
case 's':
size = atoi(optarg);
break;
case 'e':
if (opt_d) {
usage();
exit(-1);
}
opt_e = 1;
if (optarg) {
fflag = 1;
snprintf(fstr, sizeof(fstr), "%s", optarg);
fstr[sizeof(fstr) - 1] = '\0';
}
break;
case 'a':
opt_a = 1;
snprintf(ad_name, sizeof(ad_name), "%s", optarg);
ad_name[sizeof(ad_name) - 1] = '\0';
printf("Option a:%s\n", ad_name);
break;
case 'd':
snprintf(ad_name, sizeof(ad_name), "%s", optarg);
printf("adname:%s\n", ad_name);
if (opt_e) {
usage();
exit(-1);
}
opt_d = 1;
break;
case 'b': /* triggered stop after throughput drop below this threshold */
options |= ATH_PKTLOG_TRIGGER_THRUPUT;
thruput_thresh = atoi(optarg);
break;
case 'p': /* triggered stop after PER increase over this threshold */
options |= ATH_PKTLOG_TRIGGER_PER;
per_thresh = atoi(optarg);
break;
case 'y': /* triggered stop after # of phyerrs are seen */
options |= ATH_PKTLOG_TRIGGER_PHYERR;
phyerr_thresh = atoi(optarg);
break;
case 'i': /* time period of counting trigger statistics */
trigger_interval = atoi(optarg);
break;
default:
usage();
}
}
/*
* This protection is needed since system wide logging is not supported yet
*/
if (opt_e) {
if (!opt_a) {
printf("Please enter the adapter\n");
usage();
exit(-1);
}
}
if (opt_a) {
snprintf(sysctl_enable, sizeof(sysctl_enable),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/enable", ad_name);
snprintf(sysctl_size, sizeof(sysctl_size),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/size", ad_name);
snprintf(sysctl_options, sizeof(sysctl_options),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/options", ad_name);
snprintf(sysctl_sack_thr, sizeof(sysctl_sack_thr),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/sack_thr", ad_name);
snprintf(sysctl_tail_length, sizeof(sysctl_tail_length),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/tail_length", ad_name);
snprintf(sysctl_thruput_thresh, sizeof(sysctl_thruput_thresh),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/thruput_thresh", ad_name);
snprintf(sysctl_per_thresh, sizeof(sysctl_per_thresh),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/per_thresh", ad_name);
snprintf(sysctl_phyerr_thresh, sizeof(sysctl_phyerr_thresh),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/phyerr_thresh", ad_name);
snprintf(sysctl_trigger_interval, sizeof(sysctl_trigger_interval),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/trigger_interval", ad_name);
} else {
snprintf(sysctl_enable, sizeof(sysctl_enable),
"/proc/sys/" PKTLOG_PROC_DIR "/" PKTLOG_PROC_SYSTEM "/enable");
snprintf(sysctl_size, sizeof(sysctl_size),
"/proc/sys/" PKTLOG_PROC_DIR "/" PKTLOG_PROC_SYSTEM "/size");
snprintf(sysctl_options, sizeof(sysctl_options),
"/proc/sys/" PKTLOG_PROC_DIR "/" PKTLOG_PROC_SYSTEM "/options");
snprintf(sysctl_sack_thr, sizeof(sysctl_sack_thr),
"/proc/sys/" PKTLOG_PROC_DIR "/" PKTLOG_PROC_SYSTEM "/sack_thr");
snprintf(sysctl_tail_length, sizeof(sysctl_tail_length),
"/proc/sys/" PKTLOG_PROC_DIR "/" PKTLOG_PROC_SYSTEM "/tail_length");
snprintf(sysctl_thruput_thresh, sizeof(sysctl_thruput_thresh),
"/proc/sys/" PKTLOG_PROC_DIR "/" PKTLOG_PROC_SYSTEM "/thruput_thresh");
snprintf(sysctl_per_thresh, sizeof(sysctl_per_thresh),
"/proc/sys/" PKTLOG_PROC_DIR "/" PKTLOG_PROC_SYSTEM "/per_thresh");
snprintf(sysctl_phyerr_thresh, sizeof(sysctl_phyerr_thresh),
"/proc/sys/" PKTLOG_PROC_DIR "/" PKTLOG_PROC_SYSTEM "/phyerr_thresh");
snprintf(sysctl_trigger_interval, sizeof(sysctl_trigger_interval),
"/proc/sys/" PKTLOG_PROC_DIR "/"
PKTLOG_PROC_SYSTEM "/trigger_interval");
}
if (opt_d) {
/*
* Need to be removed
* Must disbale the entire system
* However doing the above does not work for individual adapter logging
* Needs fix
*/
snprintf(sysctl_enable, sizeof(sysctl_enable),
"/proc/sys/" PKTLOG_PROC_DIR "/%s/enable", ad_name);
printf("sysctl_enable: %s\n", sysctl_options);
pktlog_options(sysctl_options, 0);
pktlog_enable(sysctl_enable, 0);
printf("Called _pktlog_enable with parameter %d\n", (int) 0);
return 0;
}
if (sack_thr > 0) {
if (options & ATH_PKTLOG_PROTO) {
if (pktlog_size(sysctl_sack_thr, sysctl_enable, sack_thr) != 0) {
fprintf(stderr, "pktlogconf: log sack_thr setting failed\n");
exit(-1);
}
if (pktlog_size(sysctl_tail_length, sysctl_enable, tail_length) != 0) {
fprintf(stderr, "pktlogconf: log tail_length setting failed\n");
exit(-1);
}
} else {
usage();
exit(-1);
}
}
if (thruput_thresh > 0) {
if (pktlog_size(sysctl_thruput_thresh, sysctl_enable, thruput_thresh) != 0) {
fprintf(stderr, "pktlogconf: log thruput_thresh setting failed\n");
exit(-1);
}
}
if (per_thresh > 0) {
if (pktlog_size(sysctl_per_thresh, sysctl_enable, per_thresh) != 0) {
fprintf(stderr, "pktlogconf: log per_thresh setting failed\n");
exit(-1);
}
}
if (phyerr_thresh > 0) {
if (pktlog_size(sysctl_phyerr_thresh, sysctl_enable, phyerr_thresh) != 0) {
fprintf(stderr, "pktlogconf: log phyerr_thresh setting failed\n");
exit(-1);
}
}
if (trigger_interval > 0) {
if (pktlog_size(sysctl_trigger_interval, sysctl_enable, trigger_interval) != 0) {
fprintf(stderr, "pktlogconf: log trigger_interval setting failed\n");
exit(-1);
}
}
if (fflag) {
if (strstr(fstr, "rx"))
filter |= ATH_PKTLOG_RX;
if (strstr(fstr, "tx"))
filter |= ATH_PKTLOG_TX;
if (strstr(fstr, "rcf"))
filter |= ATH_PKTLOG_RCFIND;
if (strstr(fstr, "rcu"))
filter |= ATH_PKTLOG_RCUPDATE;
if (strstr(fstr, "ani"))
filter |= ATH_PKTLOG_ANI;
if (strstr(fstr, "text"))
filter |= ATH_PKTLOG_TEXT;
printf("_pktlog_filter:%lu\n", filter);
if (filter == 0)
usage();
} else {
filter = ATH_PKTLOG_ANI | ATH_PKTLOG_RCUPDATE | ATH_PKTLOG_RCFIND |
ATH_PKTLOG_RX | ATH_PKTLOG_TX | ATH_PKTLOG_TEXT;
printf("_pktlog_filter:%lu\n", filter);
}
if (size >= 0)
if (pktlog_size(sysctl_size, sysctl_enable, size) != 0) {
fprintf(stderr, "pktlogconf: log size setting failed\n");
exit(-1);
}
if (opt_e) {
if (pktlog_enable(sysctl_enable, filter) != 0) {
fprintf(stderr, "pktlogconf: log filter setting failed\n");
exit(-1);
}
if (pktlog_options(sysctl_options, options) != 0) {
fprintf(stderr, "pktlogconf: options setting failed\n");
exit(-1);
}
}
return 0;
}