huawei-mrd-kernel/drivers/hwnet/wifi_delayst.c

457 lines
16 KiB
C

#include <linux/ktime.h>
#include <linux/timekeeping.h>
#include <net/tcp.h>
#include <linux/sysctl.h>
#include <hwnet/ipv4/wifi_delayst.h>
#include <trace/events/skb.h>
#include <linux/cryptohash.h>
#include <linux/inet.h>
#include <huawei_platform/chr/wbc_hw_hook.h>
u8 delayst_switch = 1;
/*for sysctl command,statistic configuration*/
char tcp_delay_filter[DELAY_FILTER_NAME_MAX] = {0};
/*store statistics results*/
DELAY_STAT_T Delay_S = {{{0},{0},{0},{0},{0}},{0},{0}};
DELAY_STAT_T RcvDelay_S = {{{0},{0},{0},{0},{0}},{0},{0}};
DP_SETTINGS_T Settings = {
.dp_mode = mode_stat,
.print_interval = 1000,
.android_uid = UID_MATCH_ALL,
.dp_switch = flag_auto,
};
struct static_key wifi_delay_statistic_key __read_mostly = STATIC_KEY_INIT_FALSE;
/*operatios for delayst_skb_cb for skb->cb*/
skbdirect_enum skbprobe_get_direct(struct sk_buff *pskb)
{
return DELAYST_SKB_CB(pskb)->pdirect;
}
void skbprobe_set_direct(struct sk_buff *pskb, skbdirect_enum direct)
{
DELAYST_SKB_CB(pskb)->pdirect = direct;
}
skbtype_enum skbprobe_get_proto(struct sk_buff *pskb)
{
return DELAYST_SKB_CB(pskb)->pproto;
}
void skbprobe_set_proto(struct sk_buff *pskb, skbtype_enum proto)
{
DELAYST_SKB_CB(pskb)->pproto = proto;
}
ktime_t skbprobe_get_skbtime(struct sk_buff *pskb,int time_index)
{
return (DELAYST_SKB_CB(pskb)->ptime[time_index]);
}
__u32 skbprobe_get_skbuid(struct sk_buff *pskb)
{
return (DELAYST_SKB_CB(pskb)->android_uid);
}
void skbprobe_copy_delayskbcb(DELAYSKB_CB_T *delayskbcb,struct sk_buff *pskb)
{
if (IS_DIRECT(pskb, TP_SKB_DIRECT_SND)) {
delayskbcb->android_uid = skbprobe_get_skbuid(pskb);
delayskbcb->ptime[TP_SKB_SEND] = skbprobe_get_skbtime(pskb,TP_SKB_SEND);
delayskbcb->ptime[TP_SKB_IP] = skbprobe_get_skbtime(pskb,TP_SKB_IP);
delayskbcb->ptime[TP_SKB_HMAC_XMIT] = skbprobe_get_skbtime(pskb,TP_SKB_HMAC_XMIT);
delayskbcb->ptime[TP_SKB_HMAC_TX] = skbprobe_get_skbtime(pskb,TP_SKB_HMAC_TX);
delayskbcb->ptime[TP_SKB_DMAC] = skbprobe_get_skbtime(pskb,TP_SKB_DMAC);
} else {
delayskbcb->android_uid = skbprobe_get_skbuid(pskb);
delayskbcb->ptime[TP_SKB_DMAC] = skbprobe_get_skbtime(pskb,TP_SKB_DMAC);
delayskbcb->ptime[TP_SKB_HMAC_UPLOAD] = skbprobe_get_skbtime(pskb,TP_SKB_HMAC_UPLOAD);
delayskbcb->ptime[TP_SKB_IP] = skbprobe_get_skbtime(pskb,TP_SKB_IP);
delayskbcb->ptime[TP_SKB_RECV] = skbprobe_get_skbtime(pskb,TP_SKB_RECV);
}
}
/*0: not match ,1:match*/
int is_uid_match(struct sk_buff *skb)
{
if (0 == (DELAYST_SKB_CB(skb)->android_uid) ) {
return 0;
}
if ((UID_MATCH_ALL == Settings.android_uid) ||
((DELAYST_SKB_CB(skb)->android_uid) == Settings.android_uid)) {
return 1;
} else {
return 0;
}
}
void skbprobe_record_first(struct sk_buff *skb, u32 type)
{
if (type > (TP_SKB_MAX_ENTRY - 1)) {
return;
}
memset(DELAYST_SKB_CB(skb)->ptime, 0, sizeof(ktime_t)*TP_SKB_MAX_ENTRY);
DELAYST_SKB_CB(skb)->ptime[type] = ktime_get_real();
DELAYST_SKB_CB(skb)->pdirect = ((type == 0) ? (TP_SKB_DIRECT_SND) : (TP_SKB_DIRECT_RCV));
DELAYST_SKB_CB(skb)->android_uid = UID_MATCH_ALL;
}
void skbprobe_record_time(struct sk_buff *skb, int index)
{
if (index < 0 || index >= TP_SKB_MAX_ENTRY) {
return;
}
DELAYST_SKB_CB(skb)->ptime[index] = ktime_get_real();
}
void skbprobe_record_proto(struct sk_buff *skb, __u8 n)
{
skbprobe_set_proto(skb,n);
}
u32 skbprobe_get_latency(struct sk_buff *skb, int t1, int t2)
{
if (t1 < 0 || t2 < 0 || t1 >= TP_SKB_MAX_ENTRY || t2 >= TP_SKB_MAX_ENTRY) {
return 0;
}
return (ktime_to_us(skbprobe_get_skbtime(skb,t2)) - ktime_to_us(skbprobe_get_skbtime(skb,t1)));
}
u32 skbprobe_get_latency_from_delayst(DELAYSKB_CB_T *delayskb, int t1, int t2)
{
if (t1 < 0 || t2 < 0 || t1 >= TP_SKB_MAX_ENTRY || t2 >= TP_SKB_MAX_ENTRY) {
return 0;
}
return (ktime_to_us(delayskb->ptime[t2]) - ktime_to_us(delayskb->ptime[t1]));
}
int delay_record_get_interval_index(uint32_t uDelay)
{
const int threshold_values[4] ={50,200, 1000, 10000};
int j = 0;
for (j=0; j<4; ++j) {
if (uDelay < threshold_values[j]) {
break;
}
}
return j;
}
void delay_record_send(DELAY_STAT_T *delay, struct sk_buff *skb)
{
int i=0;
uint32_t uDelay = 0;
for (i=0; i<4; ++i) {
uDelay = skbprobe_get_latency(skb,i,i+1);
delay->T_TotalDelay[i+1] += uDelay;
delay->T_TotalPkts[i+1] += 1;
delay->T_gap[i+1][delay_record_get_interval_index(uDelay)] ++;
}
}
void delay_record_receive(DELAY_STAT_T *delay, struct sk_buff *skb)
{
int i=0;
uint32_t uDelay = 0;
for (i=3; i>0; --i) {
uDelay = skbprobe_get_latency(skb,i,i-1);
delay->T_TotalDelay[i-1] += uDelay;
delay->T_TotalPkts[i-1] += 1;
delay->T_gap[i-1][delay_record_get_interval_index(uDelay)] ++;
}
}
/*combine funtion for network layer*/
void delay_record_ip_combine(struct sk_buff *skb, skbdirect_enum direct)
{
struct iphdr *iph= NULL;
int index =TP_SKB_HMAC_UPLOAD;
delay_flow_ctl(skb);
iph = ip_hdr(skb);
if (NULL == iph) {
return;
}
if (iph->saddr == in_aton("127.0.0.1") || iph->daddr == in_aton("127.0.0.1")) {
memset(DELAYST_SKB_CB(skb)->ptime, 0, sizeof(ktime_t)*TP_SKB_MAX_ENTRY);
return;
}
if (direct == TP_SKB_DIRECT_SND) {
index = TP_SKB_SEND;
}
if (IS_NEED_RECORD_DELAY(skb,index)) {
skbprobe_record_time(skb, TP_SKB_IP);
}
}
/*determine whether we should record the packet or not in receive direction,filter function*/
void delay_record_rcv_combine(struct sk_buff *skb,struct sock *sk,skbtype_enum type)
{
DELAYST_SKB_CB(skb)->android_uid = sk->sk_uid.val;
if (IS_NEED_RECORD_DELAY(skb, TP_SKB_IP) && is_uid_match(skb)) {
skbprobe_record_time(skb, TP_SKB_RECV);
skbprobe_record_proto(skb,type);
delay_record_gap(skb);
}
}
/*determine whether we should record the packet or not in send direction,filter function*/
void delay_record_snd_combine(struct sk_buff *skb)
{
if (IS_NEED_RECORD_DELAY(skb,TP_SKB_HMAC_TX) && is_uid_match(skb)) {
skbprobe_record_time(skb, TP_SKB_DMAC);
delay_record_gap(skb);
}
}
void delay_record_gap(struct sk_buff *skb)
{
if (IS_DIRECT(skb, TP_SKB_DIRECT_SND)) {
delay_print_time_exception(skb,TP_SKB_SEND,TP_SKB_DMAC);
delay_record_send(&Delay_S, skb);
} else {
delay_print_time_exception(skb,TP_SKB_DMAC,TP_SKB_RECV);
delay_record_receive(&RcvDelay_S, skb);
}
if (Settings.dp_mode == mode_stat) {
if (0 != delayst_switch) {
delay_record_print_combine(skb);
}
} else {
trace_skb_latency(skb);
}
}
/*record first timestamp*/
void delay_record_first_combine(struct sock *sk, struct sk_buff *skb , skbdirect_enum direct , skbtype_enum type)
{
skbprobe_record_first(skb, TP_SKB_SEND);
skbprobe_record_proto(skb, type);
DELAYST_SKB_CB(skb)->android_uid = sk->sk_uid.val;
}
/*print delay time*/
void delay_record_print_combine(struct sk_buff *skb)
{
if (!delayst_switch) {
return;
}
if (IS_DIRECT(skb, TP_SKB_DIRECT_RCV)) {
if (PACKET_IS_ENOUGH_FOR_PRINT(RcvDelay_S, TP_SKB_RECV)) {
printk("SumPkts record : %u ,"
"Rcv Delay [<50us, <200us, <1ms, <10ms, >=10ms]"
"driver:[%u, %u, %u, %u, %u ]"
"ip:[%u, %u, %u, %u, %u ]"
"transport:[%u, %u, %u, %u, %u ]"
"Average: driver %u, ip %u, transport %u",
RcvDelay_S.T_TotalPkts[TP_SKB_RECV],
GET_UPLOAD_ALL, GET_RCV_IP_ALL,GET_RECV_ALL,
GET_AVG_UPLOAD, GET_RCV_AVG_IP, GET_AVG_RECV);
memset(&RcvDelay_S,0,sizeof(DELAY_STAT_T));
}
} else {
if (PACKET_IS_ENOUGH_FOR_PRINT(Delay_S, TP_SKB_DMAC)) {
printk("SumPkts record in driver: %u ,"
"Send Delay [<50us, <200us, <1ms, <10ms, >=10ms]"
"transport:[%u, %u, %u, %u, %u ]"
"ip:[%u, %u, %u, %u, %u ]"
"hmac:[%u, %u, %u, %u, %u ]"
"driver:[%u, %u, %u, %u, %u ]"
"Average: transport %u, ip %u,hmac %u,driver %u",
Delay_S.T_TotalPkts[TP_SKB_DMAC],
GET_SND_IP_ALL, GET_XMIT_ALL, GET_TX_ALL,GET_DMAC_ALL,
GET_SND_AVG_IP, GET_AVG_XMIT,GET_AVG_TX,GET_AVG_DMAC);
memset(&Delay_S,0,sizeof(DELAY_STAT_T));
}
}
}
/*for flow control ,based on pps*/
void delay_flow_ctl(struct sk_buff *skb)
{
static u32 total_pkt = 0;
static u8 delay_cnt = 0;
static s64 t_begin = 0;
s64 kt = 0;
if (!IS_DELAY_SWITCH_AUTO) {
return;
}
kt = ktime_to_us(ktime_get_real());
if (0 == t_begin) {
t_begin = kt;
}
total_pkt += 1;
if ((kt - t_begin) > 1000000) { /*statistics time is over 1s */
if ((total_pkt > DELAY_FLOW_THRESHOLD && delayst_switch) ||
(total_pkt < DELAY_FLOW_THRESHOLD && (!delayst_switch))) { /*overflow/lowflow */
delay_cnt++;
if (delay_cnt >= 3) { /*overflow/lowflow for at least 3 times */
delayst_switch = !delayst_switch;
delay_cnt = 0;
}
} else { /*not overflow/lowflow,count again*/
delay_cnt = 0;
}
total_pkt = 0;
t_begin =kt;
}
return ;
}
/* for time delay exception */
void delay_print_time_exception(struct sk_buff *skb, int t1, int t2)
{
static u8 time_delay_exceed_count_tx = 0;
static u8 time_delay_exceed_count_rx = 0;
u64 uDelay = 0;
static DELAYSKB_CB_T tx_buff;
static DELAYSKB_CB_T rx_buff;
DELAY_CHR_REPROT_T delay_excp_chr_report = {0};
uDelay = skbprobe_get_latency(skb,t1,t2);
if (IS_DIRECT(skb, TP_SKB_DIRECT_SND)) {
if ((uDelay > 7*DELAY_NORMAL_TIME) && (uDelay < 100*DELAY_NORMAL_TIME)) { /*bigger than 7 times normal delay*/
time_delay_exceed_count_tx++;
if (time_delay_exceed_count_tx >=10) {
skbprobe_copy_delayskbcb(&tx_buff,skb);
}
} else {
if (time_delay_exceed_count_tx >=10) {
delay_excp_chr_report.data_direct = TP_SKB_DIRECT_SND;
delay_excp_chr_report.exception_cnt = time_delay_exceed_count_tx;
delay_excp_chr_report.driver_delay = skbprobe_get_latency_from_delayst(&tx_buff, TP_SKB_HMAC_TX, TP_SKB_DMAC);
delay_excp_chr_report.hmac_delay = skbprobe_get_latency_from_delayst(&tx_buff, TP_SKB_HMAC_XMIT, TP_SKB_HMAC_TX);
delay_excp_chr_report.ip_delay = skbprobe_get_latency_from_delayst(&tx_buff, TP_SKB_IP, TP_SKB_HMAC_XMIT);
delay_excp_chr_report.transport_delay = skbprobe_get_latency_from_delayst(&tx_buff, TP_SKB_SEND, TP_SKB_IP);
delay_excp_chr_report.android_uid = tx_buff.android_uid;
wifi_kernel_delay_report(&delay_excp_chr_report);
printk( "time_delay_exception! TX: exception_count = %d, uid = %u, transport_delay %u, ip_delay %u,hmac_delay %u,driver_delay %u,send_time:%lld,ip_time:%lld",
time_delay_exceed_count_tx, tx_buff.android_uid, delay_excp_chr_report.transport_delay, delay_excp_chr_report.ip_delay, delay_excp_chr_report.hmac_delay,
delay_excp_chr_report.driver_delay,ktime_to_us(tx_buff.ptime[TP_SKB_SEND]),ktime_to_us(tx_buff.ptime[TP_SKB_IP]));
}
time_delay_exceed_count_tx = 0;
}
} else {
if ((uDelay > 7*DELAY_NORMAL_TIME) && (uDelay < 100*DELAY_NORMAL_TIME)) {
time_delay_exceed_count_rx++;
if(time_delay_exceed_count_rx >=10) {
skbprobe_copy_delayskbcb(&rx_buff,skb);
}
} else {
if (time_delay_exceed_count_rx >=10) {
delay_excp_chr_report.data_direct = TP_SKB_DIRECT_RCV;
delay_excp_chr_report.exception_cnt = time_delay_exceed_count_rx;
delay_excp_chr_report.driver_delay = skbprobe_get_latency_from_delayst(&rx_buff, TP_SKB_DMAC, TP_SKB_HMAC_UPLOAD);
delay_excp_chr_report.ip_delay = skbprobe_get_latency_from_delayst(&rx_buff, TP_SKB_HMAC_UPLOAD, TP_SKB_IP);
delay_excp_chr_report.transport_delay = skbprobe_get_latency_from_delayst(&rx_buff, TP_SKB_IP, TP_SKB_RECV);
delay_excp_chr_report.android_uid = rx_buff.android_uid;
wifi_kernel_delay_report(&delay_excp_chr_report);
printk("time_delay_exception! RX: exception_count = %d, uid = %u, driver_delay %u, ip_delay %u,transport_delay %u,DMAC_time:%lld,UPLOAD_time:%lld",
time_delay_exceed_count_rx , rx_buff.android_uid,delay_excp_chr_report.driver_delay,delay_excp_chr_report.ip_delay,delay_excp_chr_report.transport_delay,
ktime_to_us(rx_buff.ptime[TP_SKB_DMAC]),ktime_to_us(rx_buff.ptime[TP_SKB_HMAC_UPLOAD]));
}
time_delay_exceed_count_rx = 0;
}
}
return;
}
int delay_set_wifi_command(char *val)
{
char *clone = NULL, *sub_cmd = NULL;
int err = 0;
unsigned int uid = 0, interval = 0;
const char flag_names[][20] = {"flag_off", "flag_on", "flag_auto"};
const char mode_names[][20] = {"mode_stat", "mode_trace"};
if (!val)
return -EFAULT; /* Bad address */
clone = kstrdup(val, GFP_USER);
if (!clone) {
return -EFAULT;
}
switch (clone[0])
{
case 'f':
if (strcmp(clone,"flag_on") == 0) {
Settings.dp_switch = flag_on;
static_key_enable(&wifi_delay_statistic_key);
delayst_switch = 1;
} else if (strcmp(clone,"flag_off") == 0) {
Settings.dp_switch = flag_off;
static_key_disable(&wifi_delay_statistic_key);
delayst_switch = 0;
} else if (strcmp(clone,"flag_auto") == 0) {
Settings.dp_switch = flag_auto;
delayst_switch = 1;
}
break;
case 'm':
if (strcmp(clone,"mode_stat") == 0) {
Settings.dp_mode = mode_stat;
} else if(strcmp(clone,"mode_trace") == 0) {
Settings.dp_mode = mode_trace;
}
break;
case 'u':
sub_cmd = strchr((const char *)clone, '_');
if (NULL == sub_cmd) {
return -EFAULT;
}
sub_cmd++;
err = kstrtouint((const char *)sub_cmd, 10, &uid);
if (!err) {
Settings.android_uid = uid;
CLEAN_DELAY_RECORD;
}
break;
case 'p':
sub_cmd = strchr((const char *)clone, '_');
if (NULL == sub_cmd) {
return -EFAULT;
}
sub_cmd++;
err = kstrtouint((const char *)sub_cmd, 10, &interval);
if (!err) {
Settings.print_interval = interval;
CLEAN_DELAY_RECORD;
}
break;
case 'd':
CLEAN_DELAY_RECORD;
break;
case 'c':
printk("delay_command configure:delay_switch = %s,dealy_mode = %s,print_interval = %u,android_uid = %u",
flag_names[Settings.dp_switch],mode_names[Settings.dp_mode],Settings.print_interval,Settings.android_uid);
break;
default:
break;
}
if (clone)
kfree(clone);
return 0;
}
int proc_wifi_delay_command(struct ctl_table *ctl, int write, void __user *buffer,
size_t *lenp, loff_t *ppos)
{
char val[DELAY_FILTER_NAME_MAX];
struct ctl_table tbl = {
.data = val,
.maxlen = DELAY_FILTER_NAME_MAX,
};
int err = 0;
if (write) {
err = proc_dostring(&tbl, write, buffer, lenp, ppos);
if (!err) {
err = delay_set_wifi_command(val);
}
}
return err;
}