18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016 Qualcomm Atheros, Inc 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on net/sched/sch_fq_codel.c 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#ifndef __NET_SCHED_FQ_IMPL_H 88c2ecf20Sopenharmony_ci#define __NET_SCHED_FQ_IMPL_H 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <net/fq.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* functions that are embedded into includer */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic void fq_adjust_removal(struct fq *fq, 158c2ecf20Sopenharmony_ci struct fq_flow *flow, 168c2ecf20Sopenharmony_ci struct sk_buff *skb) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci struct fq_tin *tin = flow->tin; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci tin->backlog_bytes -= skb->len; 218c2ecf20Sopenharmony_ci tin->backlog_packets--; 228c2ecf20Sopenharmony_ci flow->backlog -= skb->len; 238c2ecf20Sopenharmony_ci fq->backlog--; 248c2ecf20Sopenharmony_ci fq->memory_usage -= skb->truesize; 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void fq_rejigger_backlog(struct fq *fq, struct fq_flow *flow) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct fq_flow *i; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (flow->backlog == 0) { 328c2ecf20Sopenharmony_ci list_del_init(&flow->backlogchain); 338c2ecf20Sopenharmony_ci } else { 348c2ecf20Sopenharmony_ci i = flow; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci list_for_each_entry_continue(i, &fq->backlogs, backlogchain) 378c2ecf20Sopenharmony_ci if (i->backlog < flow->backlog) 388c2ecf20Sopenharmony_ci break; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci list_move_tail(&flow->backlogchain, 418c2ecf20Sopenharmony_ci &i->backlogchain); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct sk_buff *fq_flow_dequeue(struct fq *fq, 468c2ecf20Sopenharmony_ci struct fq_flow *flow) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct sk_buff *skb; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci lockdep_assert_held(&fq->lock); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci skb = __skb_dequeue(&flow->queue); 538c2ecf20Sopenharmony_ci if (!skb) 548c2ecf20Sopenharmony_ci return NULL; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci fq_adjust_removal(fq, flow, skb); 578c2ecf20Sopenharmony_ci fq_rejigger_backlog(fq, flow); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return skb; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct sk_buff *fq_tin_dequeue(struct fq *fq, 638c2ecf20Sopenharmony_ci struct fq_tin *tin, 648c2ecf20Sopenharmony_ci fq_tin_dequeue_t dequeue_func) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct fq_flow *flow; 678c2ecf20Sopenharmony_ci struct list_head *head; 688c2ecf20Sopenharmony_ci struct sk_buff *skb; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci lockdep_assert_held(&fq->lock); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cibegin: 738c2ecf20Sopenharmony_ci head = &tin->new_flows; 748c2ecf20Sopenharmony_ci if (list_empty(head)) { 758c2ecf20Sopenharmony_ci head = &tin->old_flows; 768c2ecf20Sopenharmony_ci if (list_empty(head)) 778c2ecf20Sopenharmony_ci return NULL; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci flow = list_first_entry(head, struct fq_flow, flowchain); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (flow->deficit <= 0) { 838c2ecf20Sopenharmony_ci flow->deficit += fq->quantum; 848c2ecf20Sopenharmony_ci list_move_tail(&flow->flowchain, 858c2ecf20Sopenharmony_ci &tin->old_flows); 868c2ecf20Sopenharmony_ci goto begin; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci skb = dequeue_func(fq, tin, flow); 908c2ecf20Sopenharmony_ci if (!skb) { 918c2ecf20Sopenharmony_ci /* force a pass through old_flows to prevent starvation */ 928c2ecf20Sopenharmony_ci if ((head == &tin->new_flows) && 938c2ecf20Sopenharmony_ci !list_empty(&tin->old_flows)) { 948c2ecf20Sopenharmony_ci list_move_tail(&flow->flowchain, &tin->old_flows); 958c2ecf20Sopenharmony_ci } else { 968c2ecf20Sopenharmony_ci list_del_init(&flow->flowchain); 978c2ecf20Sopenharmony_ci flow->tin = NULL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci goto begin; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci flow->deficit -= skb->len; 1038c2ecf20Sopenharmony_ci tin->tx_bytes += skb->len; 1048c2ecf20Sopenharmony_ci tin->tx_packets++; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return skb; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic u32 fq_flow_idx(struct fq *fq, struct sk_buff *skb) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci u32 hash = skb_get_hash(skb); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return reciprocal_scale(hash, fq->flows_cnt); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic struct fq_flow *fq_flow_classify(struct fq *fq, 1178c2ecf20Sopenharmony_ci struct fq_tin *tin, u32 idx, 1188c2ecf20Sopenharmony_ci struct sk_buff *skb, 1198c2ecf20Sopenharmony_ci fq_flow_get_default_t get_default_func) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct fq_flow *flow; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci lockdep_assert_held(&fq->lock); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci flow = &fq->flows[idx]; 1268c2ecf20Sopenharmony_ci if (flow->tin && flow->tin != tin) { 1278c2ecf20Sopenharmony_ci flow = get_default_func(fq, tin, idx, skb); 1288c2ecf20Sopenharmony_ci tin->collisions++; 1298c2ecf20Sopenharmony_ci fq->collisions++; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!flow->tin) 1338c2ecf20Sopenharmony_ci tin->flows++; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return flow; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void fq_recalc_backlog(struct fq *fq, 1398c2ecf20Sopenharmony_ci struct fq_tin *tin, 1408c2ecf20Sopenharmony_ci struct fq_flow *flow) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct fq_flow *i; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (list_empty(&flow->backlogchain)) 1458c2ecf20Sopenharmony_ci list_add_tail(&flow->backlogchain, &fq->backlogs); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci i = flow; 1488c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(i, &fq->backlogs, 1498c2ecf20Sopenharmony_ci backlogchain) 1508c2ecf20Sopenharmony_ci if (i->backlog > flow->backlog) 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci list_move(&flow->backlogchain, &i->backlogchain); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void fq_tin_enqueue(struct fq *fq, 1578c2ecf20Sopenharmony_ci struct fq_tin *tin, u32 idx, 1588c2ecf20Sopenharmony_ci struct sk_buff *skb, 1598c2ecf20Sopenharmony_ci fq_skb_free_t free_func, 1608c2ecf20Sopenharmony_ci fq_flow_get_default_t get_default_func) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct fq_flow *flow; 1638c2ecf20Sopenharmony_ci bool oom; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci lockdep_assert_held(&fq->lock); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci flow = fq_flow_classify(fq, tin, idx, skb, get_default_func); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci flow->tin = tin; 1708c2ecf20Sopenharmony_ci flow->backlog += skb->len; 1718c2ecf20Sopenharmony_ci tin->backlog_bytes += skb->len; 1728c2ecf20Sopenharmony_ci tin->backlog_packets++; 1738c2ecf20Sopenharmony_ci fq->memory_usage += skb->truesize; 1748c2ecf20Sopenharmony_ci fq->backlog++; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci fq_recalc_backlog(fq, tin, flow); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (list_empty(&flow->flowchain)) { 1798c2ecf20Sopenharmony_ci flow->deficit = fq->quantum; 1808c2ecf20Sopenharmony_ci list_add_tail(&flow->flowchain, 1818c2ecf20Sopenharmony_ci &tin->new_flows); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci __skb_queue_tail(&flow->queue, skb); 1858c2ecf20Sopenharmony_ci oom = (fq->memory_usage > fq->memory_limit); 1868c2ecf20Sopenharmony_ci while (fq->backlog > fq->limit || oom) { 1878c2ecf20Sopenharmony_ci flow = list_first_entry_or_null(&fq->backlogs, 1888c2ecf20Sopenharmony_ci struct fq_flow, 1898c2ecf20Sopenharmony_ci backlogchain); 1908c2ecf20Sopenharmony_ci if (!flow) 1918c2ecf20Sopenharmony_ci return; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci skb = fq_flow_dequeue(fq, flow); 1948c2ecf20Sopenharmony_ci if (!skb) 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci free_func(fq, flow->tin, flow, skb); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci flow->tin->overlimit++; 2008c2ecf20Sopenharmony_ci fq->overlimit++; 2018c2ecf20Sopenharmony_ci if (oom) { 2028c2ecf20Sopenharmony_ci fq->overmemory++; 2038c2ecf20Sopenharmony_ci oom = (fq->memory_usage > fq->memory_limit); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void fq_flow_filter(struct fq *fq, 2098c2ecf20Sopenharmony_ci struct fq_flow *flow, 2108c2ecf20Sopenharmony_ci fq_skb_filter_t filter_func, 2118c2ecf20Sopenharmony_ci void *filter_data, 2128c2ecf20Sopenharmony_ci fq_skb_free_t free_func) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct fq_tin *tin = flow->tin; 2158c2ecf20Sopenharmony_ci struct sk_buff *skb, *tmp; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci lockdep_assert_held(&fq->lock); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci skb_queue_walk_safe(&flow->queue, skb, tmp) { 2208c2ecf20Sopenharmony_ci if (!filter_func(fq, tin, flow, skb, filter_data)) 2218c2ecf20Sopenharmony_ci continue; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci __skb_unlink(skb, &flow->queue); 2248c2ecf20Sopenharmony_ci fq_adjust_removal(fq, flow, skb); 2258c2ecf20Sopenharmony_ci free_func(fq, tin, flow, skb); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci fq_rejigger_backlog(fq, flow); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void fq_tin_filter(struct fq *fq, 2328c2ecf20Sopenharmony_ci struct fq_tin *tin, 2338c2ecf20Sopenharmony_ci fq_skb_filter_t filter_func, 2348c2ecf20Sopenharmony_ci void *filter_data, 2358c2ecf20Sopenharmony_ci fq_skb_free_t free_func) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct fq_flow *flow; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci lockdep_assert_held(&fq->lock); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci list_for_each_entry(flow, &tin->new_flows, flowchain) 2428c2ecf20Sopenharmony_ci fq_flow_filter(fq, flow, filter_func, filter_data, free_func); 2438c2ecf20Sopenharmony_ci list_for_each_entry(flow, &tin->old_flows, flowchain) 2448c2ecf20Sopenharmony_ci fq_flow_filter(fq, flow, filter_func, filter_data, free_func); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void fq_flow_reset(struct fq *fq, 2488c2ecf20Sopenharmony_ci struct fq_flow *flow, 2498c2ecf20Sopenharmony_ci fq_skb_free_t free_func) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct sk_buff *skb; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci while ((skb = fq_flow_dequeue(fq, flow))) 2548c2ecf20Sopenharmony_ci free_func(fq, flow->tin, flow, skb); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (!list_empty(&flow->flowchain)) 2578c2ecf20Sopenharmony_ci list_del_init(&flow->flowchain); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!list_empty(&flow->backlogchain)) 2608c2ecf20Sopenharmony_ci list_del_init(&flow->backlogchain); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci flow->tin = NULL; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci WARN_ON_ONCE(flow->backlog); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void fq_tin_reset(struct fq *fq, 2688c2ecf20Sopenharmony_ci struct fq_tin *tin, 2698c2ecf20Sopenharmony_ci fq_skb_free_t free_func) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct list_head *head; 2728c2ecf20Sopenharmony_ci struct fq_flow *flow; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci for (;;) { 2758c2ecf20Sopenharmony_ci head = &tin->new_flows; 2768c2ecf20Sopenharmony_ci if (list_empty(head)) { 2778c2ecf20Sopenharmony_ci head = &tin->old_flows; 2788c2ecf20Sopenharmony_ci if (list_empty(head)) 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci flow = list_first_entry(head, struct fq_flow, flowchain); 2838c2ecf20Sopenharmony_ci fq_flow_reset(fq, flow, free_func); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci WARN_ON_ONCE(tin->backlog_bytes); 2878c2ecf20Sopenharmony_ci WARN_ON_ONCE(tin->backlog_packets); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void fq_flow_init(struct fq_flow *flow) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&flow->flowchain); 2938c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&flow->backlogchain); 2948c2ecf20Sopenharmony_ci __skb_queue_head_init(&flow->queue); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void fq_tin_init(struct fq_tin *tin) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tin->new_flows); 3008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tin->old_flows); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int fq_init(struct fq *fq, int flows_cnt) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int i; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci memset(fq, 0, sizeof(fq[0])); 3088c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&fq->backlogs); 3098c2ecf20Sopenharmony_ci spin_lock_init(&fq->lock); 3108c2ecf20Sopenharmony_ci fq->flows_cnt = max_t(u32, flows_cnt, 1); 3118c2ecf20Sopenharmony_ci fq->quantum = 300; 3128c2ecf20Sopenharmony_ci fq->limit = 8192; 3138c2ecf20Sopenharmony_ci fq->memory_limit = 16 << 20; /* 16 MBytes */ 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci fq->flows = kvcalloc(fq->flows_cnt, sizeof(fq->flows[0]), GFP_KERNEL); 3168c2ecf20Sopenharmony_ci if (!fq->flows) 3178c2ecf20Sopenharmony_ci return -ENOMEM; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci for (i = 0; i < fq->flows_cnt; i++) 3208c2ecf20Sopenharmony_ci fq_flow_init(&fq->flows[i]); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void fq_reset(struct fq *fq, 3268c2ecf20Sopenharmony_ci fq_skb_free_t free_func) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci int i; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci for (i = 0; i < fq->flows_cnt; i++) 3318c2ecf20Sopenharmony_ci fq_flow_reset(fq, &fq->flows[i], free_func); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci kvfree(fq->flows); 3348c2ecf20Sopenharmony_ci fq->flows = NULL; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci#endif 338