18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Fair Queue CoDel discipline 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012,2015 Eric Dumazet <edumazet@google.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/in.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 198c2ecf20Sopenharmony_ci#include <net/netlink.h> 208c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 218c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 228c2ecf20Sopenharmony_ci#include <net/codel.h> 238c2ecf20Sopenharmony_ci#include <net/codel_impl.h> 248c2ecf20Sopenharmony_ci#include <net/codel_qdisc.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Fair Queue CoDel. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Principles : 298c2ecf20Sopenharmony_ci * Packets are classified (internal classifier or external) on flows. 308c2ecf20Sopenharmony_ci * This is a Stochastic model (as we use a hash, several flows 318c2ecf20Sopenharmony_ci * might be hashed on same slot) 328c2ecf20Sopenharmony_ci * Each flow has a CoDel managed queue. 338c2ecf20Sopenharmony_ci * Flows are linked onto two (Round Robin) lists, 348c2ecf20Sopenharmony_ci * so that new flows have priority on old ones. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * For a given flow, packets are not reordered (CoDel uses a FIFO) 378c2ecf20Sopenharmony_ci * head drops only. 388c2ecf20Sopenharmony_ci * ECN capability is on by default. 398c2ecf20Sopenharmony_ci * Low memory footprint (64 bytes per flow) 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct fq_codel_flow { 438c2ecf20Sopenharmony_ci struct sk_buff *head; 448c2ecf20Sopenharmony_ci struct sk_buff *tail; 458c2ecf20Sopenharmony_ci struct list_head flowchain; 468c2ecf20Sopenharmony_ci int deficit; 478c2ecf20Sopenharmony_ci struct codel_vars cvars; 488c2ecf20Sopenharmony_ci}; /* please try to keep this structure <= 64 bytes */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct fq_codel_sched_data { 518c2ecf20Sopenharmony_ci struct tcf_proto __rcu *filter_list; /* optional external classifier */ 528c2ecf20Sopenharmony_ci struct tcf_block *block; 538c2ecf20Sopenharmony_ci struct fq_codel_flow *flows; /* Flows table [flows_cnt] */ 548c2ecf20Sopenharmony_ci u32 *backlogs; /* backlog table [flows_cnt] */ 558c2ecf20Sopenharmony_ci u32 flows_cnt; /* number of flows */ 568c2ecf20Sopenharmony_ci u32 quantum; /* psched_mtu(qdisc_dev(sch)); */ 578c2ecf20Sopenharmony_ci u32 drop_batch_size; 588c2ecf20Sopenharmony_ci u32 memory_limit; 598c2ecf20Sopenharmony_ci struct codel_params cparams; 608c2ecf20Sopenharmony_ci struct codel_stats cstats; 618c2ecf20Sopenharmony_ci u32 memory_usage; 628c2ecf20Sopenharmony_ci u32 drop_overmemory; 638c2ecf20Sopenharmony_ci u32 drop_overlimit; 648c2ecf20Sopenharmony_ci u32 new_flow_count; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci struct list_head new_flows; /* list of new flows */ 678c2ecf20Sopenharmony_ci struct list_head old_flows; /* list of old flows */ 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic unsigned int fq_codel_hash(const struct fq_codel_sched_data *q, 718c2ecf20Sopenharmony_ci struct sk_buff *skb) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci return reciprocal_scale(skb_get_hash(skb), q->flows_cnt); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch, 778c2ecf20Sopenharmony_ci int *qerr) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 808c2ecf20Sopenharmony_ci struct tcf_proto *filter; 818c2ecf20Sopenharmony_ci struct tcf_result res; 828c2ecf20Sopenharmony_ci int result; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (TC_H_MAJ(skb->priority) == sch->handle && 858c2ecf20Sopenharmony_ci TC_H_MIN(skb->priority) > 0 && 868c2ecf20Sopenharmony_ci TC_H_MIN(skb->priority) <= q->flows_cnt) 878c2ecf20Sopenharmony_ci return TC_H_MIN(skb->priority); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci filter = rcu_dereference_bh(q->filter_list); 908c2ecf20Sopenharmony_ci if (!filter) 918c2ecf20Sopenharmony_ci return fq_codel_hash(q, skb) + 1; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; 948c2ecf20Sopenharmony_ci result = tcf_classify(skb, filter, &res, false); 958c2ecf20Sopenharmony_ci if (result >= 0) { 968c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT 978c2ecf20Sopenharmony_ci switch (result) { 988c2ecf20Sopenharmony_ci case TC_ACT_STOLEN: 998c2ecf20Sopenharmony_ci case TC_ACT_QUEUED: 1008c2ecf20Sopenharmony_ci case TC_ACT_TRAP: 1018c2ecf20Sopenharmony_ci *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; 1028c2ecf20Sopenharmony_ci fallthrough; 1038c2ecf20Sopenharmony_ci case TC_ACT_SHOT: 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci if (TC_H_MIN(res.classid) <= q->flows_cnt) 1088c2ecf20Sopenharmony_ci return TC_H_MIN(res.classid); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* helper functions : might be changed when/if skb use a standard list_head */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* remove one skb from head of slot queue */ 1168c2ecf20Sopenharmony_cistatic inline struct sk_buff *dequeue_head(struct fq_codel_flow *flow) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct sk_buff *skb = flow->head; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci flow->head = skb->next; 1218c2ecf20Sopenharmony_ci skb_mark_not_on_list(skb); 1228c2ecf20Sopenharmony_ci return skb; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* add skb to flow queue (tail add) */ 1268c2ecf20Sopenharmony_cistatic inline void flow_queue_add(struct fq_codel_flow *flow, 1278c2ecf20Sopenharmony_ci struct sk_buff *skb) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci if (flow->head == NULL) 1308c2ecf20Sopenharmony_ci flow->head = skb; 1318c2ecf20Sopenharmony_ci else 1328c2ecf20Sopenharmony_ci flow->tail->next = skb; 1338c2ecf20Sopenharmony_ci flow->tail = skb; 1348c2ecf20Sopenharmony_ci skb->next = NULL; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets, 1388c2ecf20Sopenharmony_ci struct sk_buff **to_free) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 1418c2ecf20Sopenharmony_ci struct sk_buff *skb; 1428c2ecf20Sopenharmony_ci unsigned int maxbacklog = 0, idx = 0, i, len; 1438c2ecf20Sopenharmony_ci struct fq_codel_flow *flow; 1448c2ecf20Sopenharmony_ci unsigned int threshold; 1458c2ecf20Sopenharmony_ci unsigned int mem = 0; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Queue is full! Find the fat flow and drop packet(s) from it. 1488c2ecf20Sopenharmony_ci * This might sound expensive, but with 1024 flows, we scan 1498c2ecf20Sopenharmony_ci * 4KB of memory, and we dont need to handle a complex tree 1508c2ecf20Sopenharmony_ci * in fast path (packet queue/enqueue) with many cache misses. 1518c2ecf20Sopenharmony_ci * In stress mode, we'll try to drop 64 packets from the flow, 1528c2ecf20Sopenharmony_ci * amortizing this linear lookup to one cache line per drop. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci for (i = 0; i < q->flows_cnt; i++) { 1558c2ecf20Sopenharmony_ci if (q->backlogs[i] > maxbacklog) { 1568c2ecf20Sopenharmony_ci maxbacklog = q->backlogs[i]; 1578c2ecf20Sopenharmony_ci idx = i; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Our goal is to drop half of this fat flow backlog */ 1628c2ecf20Sopenharmony_ci threshold = maxbacklog >> 1; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci flow = &q->flows[idx]; 1658c2ecf20Sopenharmony_ci len = 0; 1668c2ecf20Sopenharmony_ci i = 0; 1678c2ecf20Sopenharmony_ci do { 1688c2ecf20Sopenharmony_ci skb = dequeue_head(flow); 1698c2ecf20Sopenharmony_ci len += qdisc_pkt_len(skb); 1708c2ecf20Sopenharmony_ci mem += get_codel_cb(skb)->mem_usage; 1718c2ecf20Sopenharmony_ci __qdisc_drop(skb, to_free); 1728c2ecf20Sopenharmony_ci } while (++i < max_packets && len < threshold); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Tell codel to increase its signal strength also */ 1758c2ecf20Sopenharmony_ci flow->cvars.count += i; 1768c2ecf20Sopenharmony_ci q->backlogs[idx] -= len; 1778c2ecf20Sopenharmony_ci q->memory_usage -= mem; 1788c2ecf20Sopenharmony_ci sch->qstats.drops += i; 1798c2ecf20Sopenharmony_ci sch->qstats.backlog -= len; 1808c2ecf20Sopenharmony_ci sch->q.qlen -= i; 1818c2ecf20Sopenharmony_ci return idx; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch, 1858c2ecf20Sopenharmony_ci struct sk_buff **to_free) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 1888c2ecf20Sopenharmony_ci unsigned int idx, prev_backlog, prev_qlen; 1898c2ecf20Sopenharmony_ci struct fq_codel_flow *flow; 1908c2ecf20Sopenharmony_ci int ret; 1918c2ecf20Sopenharmony_ci unsigned int pkt_len; 1928c2ecf20Sopenharmony_ci bool memory_limited; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci idx = fq_codel_classify(skb, sch, &ret); 1958c2ecf20Sopenharmony_ci if (idx == 0) { 1968c2ecf20Sopenharmony_ci if (ret & __NET_XMIT_BYPASS) 1978c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 1988c2ecf20Sopenharmony_ci __qdisc_drop(skb, to_free); 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci idx--; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci codel_set_enqueue_time(skb); 2048c2ecf20Sopenharmony_ci flow = &q->flows[idx]; 2058c2ecf20Sopenharmony_ci flow_queue_add(flow, skb); 2068c2ecf20Sopenharmony_ci q->backlogs[idx] += qdisc_pkt_len(skb); 2078c2ecf20Sopenharmony_ci qdisc_qstats_backlog_inc(sch, skb); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (list_empty(&flow->flowchain)) { 2108c2ecf20Sopenharmony_ci list_add_tail(&flow->flowchain, &q->new_flows); 2118c2ecf20Sopenharmony_ci q->new_flow_count++; 2128c2ecf20Sopenharmony_ci flow->deficit = q->quantum; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci get_codel_cb(skb)->mem_usage = skb->truesize; 2158c2ecf20Sopenharmony_ci q->memory_usage += get_codel_cb(skb)->mem_usage; 2168c2ecf20Sopenharmony_ci memory_limited = q->memory_usage > q->memory_limit; 2178c2ecf20Sopenharmony_ci if (++sch->q.qlen <= sch->limit && !memory_limited) 2188c2ecf20Sopenharmony_ci return NET_XMIT_SUCCESS; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci prev_backlog = sch->qstats.backlog; 2218c2ecf20Sopenharmony_ci prev_qlen = sch->q.qlen; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* save this packet length as it might be dropped by fq_codel_drop() */ 2248c2ecf20Sopenharmony_ci pkt_len = qdisc_pkt_len(skb); 2258c2ecf20Sopenharmony_ci /* fq_codel_drop() is quite expensive, as it performs a linear search 2268c2ecf20Sopenharmony_ci * in q->backlogs[] to find a fat flow. 2278c2ecf20Sopenharmony_ci * So instead of dropping a single packet, drop half of its backlog 2288c2ecf20Sopenharmony_ci * with a 64 packets limit to not add a too big cpu spike here. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci ret = fq_codel_drop(sch, q->drop_batch_size, to_free); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci prev_qlen -= sch->q.qlen; 2338c2ecf20Sopenharmony_ci prev_backlog -= sch->qstats.backlog; 2348c2ecf20Sopenharmony_ci q->drop_overlimit += prev_qlen; 2358c2ecf20Sopenharmony_ci if (memory_limited) 2368c2ecf20Sopenharmony_ci q->drop_overmemory += prev_qlen; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* As we dropped packet(s), better let upper stack know this. 2398c2ecf20Sopenharmony_ci * If we dropped a packet for this flow, return NET_XMIT_CN, 2408c2ecf20Sopenharmony_ci * but in this case, our parents wont increase their backlogs. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci if (ret == idx) { 2438c2ecf20Sopenharmony_ci qdisc_tree_reduce_backlog(sch, prev_qlen - 1, 2448c2ecf20Sopenharmony_ci prev_backlog - pkt_len); 2458c2ecf20Sopenharmony_ci return NET_XMIT_CN; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci qdisc_tree_reduce_backlog(sch, prev_qlen, prev_backlog); 2488c2ecf20Sopenharmony_ci return NET_XMIT_SUCCESS; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* This is the specific function called from codel_dequeue() 2528c2ecf20Sopenharmony_ci * to dequeue a packet from queue. Note: backlog is handled in 2538c2ecf20Sopenharmony_ci * codel, we dont need to reduce it here. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_cistatic struct sk_buff *dequeue_func(struct codel_vars *vars, void *ctx) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct Qdisc *sch = ctx; 2588c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 2598c2ecf20Sopenharmony_ci struct fq_codel_flow *flow; 2608c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci flow = container_of(vars, struct fq_codel_flow, cvars); 2638c2ecf20Sopenharmony_ci if (flow->head) { 2648c2ecf20Sopenharmony_ci skb = dequeue_head(flow); 2658c2ecf20Sopenharmony_ci q->backlogs[flow - q->flows] -= qdisc_pkt_len(skb); 2668c2ecf20Sopenharmony_ci q->memory_usage -= get_codel_cb(skb)->mem_usage; 2678c2ecf20Sopenharmony_ci sch->q.qlen--; 2688c2ecf20Sopenharmony_ci sch->qstats.backlog -= qdisc_pkt_len(skb); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci return skb; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void drop_func(struct sk_buff *skb, void *ctx) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct Qdisc *sch = ctx; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci kfree_skb(skb); 2788c2ecf20Sopenharmony_ci qdisc_qstats_drop(sch); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 2848c2ecf20Sopenharmony_ci struct sk_buff *skb; 2858c2ecf20Sopenharmony_ci struct fq_codel_flow *flow; 2868c2ecf20Sopenharmony_ci struct list_head *head; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cibegin: 2898c2ecf20Sopenharmony_ci head = &q->new_flows; 2908c2ecf20Sopenharmony_ci if (list_empty(head)) { 2918c2ecf20Sopenharmony_ci head = &q->old_flows; 2928c2ecf20Sopenharmony_ci if (list_empty(head)) 2938c2ecf20Sopenharmony_ci return NULL; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci flow = list_first_entry(head, struct fq_codel_flow, flowchain); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (flow->deficit <= 0) { 2988c2ecf20Sopenharmony_ci flow->deficit += q->quantum; 2998c2ecf20Sopenharmony_ci list_move_tail(&flow->flowchain, &q->old_flows); 3008c2ecf20Sopenharmony_ci goto begin; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci skb = codel_dequeue(sch, &sch->qstats.backlog, &q->cparams, 3048c2ecf20Sopenharmony_ci &flow->cvars, &q->cstats, qdisc_pkt_len, 3058c2ecf20Sopenharmony_ci codel_get_enqueue_time, drop_func, dequeue_func); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!skb) { 3088c2ecf20Sopenharmony_ci /* force a pass through old_flows to prevent starvation */ 3098c2ecf20Sopenharmony_ci if ((head == &q->new_flows) && !list_empty(&q->old_flows)) 3108c2ecf20Sopenharmony_ci list_move_tail(&flow->flowchain, &q->old_flows); 3118c2ecf20Sopenharmony_ci else 3128c2ecf20Sopenharmony_ci list_del_init(&flow->flowchain); 3138c2ecf20Sopenharmony_ci goto begin; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci qdisc_bstats_update(sch, skb); 3168c2ecf20Sopenharmony_ci flow->deficit -= qdisc_pkt_len(skb); 3178c2ecf20Sopenharmony_ci /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, 3188c2ecf20Sopenharmony_ci * or HTB crashes. Defer it for next round. 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_ci if (q->cstats.drop_count && sch->q.qlen) { 3218c2ecf20Sopenharmony_ci qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, 3228c2ecf20Sopenharmony_ci q->cstats.drop_len); 3238c2ecf20Sopenharmony_ci q->cstats.drop_count = 0; 3248c2ecf20Sopenharmony_ci q->cstats.drop_len = 0; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci return skb; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void fq_codel_flow_purge(struct fq_codel_flow *flow) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci rtnl_kfree_skbs(flow->head, flow->tail); 3328c2ecf20Sopenharmony_ci flow->head = NULL; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic void fq_codel_reset(struct Qdisc *sch) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 3388c2ecf20Sopenharmony_ci int i; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&q->new_flows); 3418c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&q->old_flows); 3428c2ecf20Sopenharmony_ci for (i = 0; i < q->flows_cnt; i++) { 3438c2ecf20Sopenharmony_ci struct fq_codel_flow *flow = q->flows + i; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci fq_codel_flow_purge(flow); 3468c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&flow->flowchain); 3478c2ecf20Sopenharmony_ci codel_vars_init(&flow->cvars); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci memset(q->backlogs, 0, q->flows_cnt * sizeof(u32)); 3508c2ecf20Sopenharmony_ci q->memory_usage = 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic const struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = { 3548c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_TARGET] = { .type = NLA_U32 }, 3558c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_LIMIT] = { .type = NLA_U32 }, 3568c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 }, 3578c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_ECN] = { .type = NLA_U32 }, 3588c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_FLOWS] = { .type = NLA_U32 }, 3598c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 }, 3608c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_CE_THRESHOLD] = { .type = NLA_U32 }, 3618c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NLA_U32 }, 3628c2ecf20Sopenharmony_ci [TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NLA_U32 }, 3638c2ecf20Sopenharmony_ci}; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int fq_codel_change(struct Qdisc *sch, struct nlattr *opt, 3668c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 3698c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_FQ_CODEL_MAX + 1]; 3708c2ecf20Sopenharmony_ci u32 quantum = 0; 3718c2ecf20Sopenharmony_ci int err; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (!opt) 3748c2ecf20Sopenharmony_ci return -EINVAL; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_FQ_CODEL_MAX, opt, 3778c2ecf20Sopenharmony_ci fq_codel_policy, NULL); 3788c2ecf20Sopenharmony_ci if (err < 0) 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_FLOWS]) { 3818c2ecf20Sopenharmony_ci if (q->flows) 3828c2ecf20Sopenharmony_ci return -EINVAL; 3838c2ecf20Sopenharmony_ci q->flows_cnt = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]); 3848c2ecf20Sopenharmony_ci if (!q->flows_cnt || 3858c2ecf20Sopenharmony_ci q->flows_cnt > 65536) 3868c2ecf20Sopenharmony_ci return -EINVAL; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_QUANTUM]) { 3898c2ecf20Sopenharmony_ci quantum = max(256U, nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM])); 3908c2ecf20Sopenharmony_ci if (quantum > FQ_CODEL_QUANTUM_MAX) { 3918c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid quantum"); 3928c2ecf20Sopenharmony_ci return -EINVAL; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci sch_tree_lock(sch); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_TARGET]) { 3988c2ecf20Sopenharmony_ci u64 target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci q->cparams.target = (target * NSEC_PER_USEC) >> CODEL_SHIFT; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_CE_THRESHOLD]) { 4048c2ecf20Sopenharmony_ci u64 val = nla_get_u32(tb[TCA_FQ_CODEL_CE_THRESHOLD]); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci q->cparams.ce_threshold = (val * NSEC_PER_USEC) >> CODEL_SHIFT; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_INTERVAL]) { 4108c2ecf20Sopenharmony_ci u64 interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci q->cparams.interval = (interval * NSEC_PER_USEC) >> CODEL_SHIFT; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_LIMIT]) 4168c2ecf20Sopenharmony_ci sch->limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_ECN]) 4198c2ecf20Sopenharmony_ci q->cparams.ecn = !!nla_get_u32(tb[TCA_FQ_CODEL_ECN]); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (quantum) 4228c2ecf20Sopenharmony_ci q->quantum = quantum; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]) 4258c2ecf20Sopenharmony_ci q->drop_batch_size = max(1U, nla_get_u32(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE])); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (tb[TCA_FQ_CODEL_MEMORY_LIMIT]) 4288c2ecf20Sopenharmony_ci q->memory_limit = min(1U << 31, nla_get_u32(tb[TCA_FQ_CODEL_MEMORY_LIMIT])); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci while (sch->q.qlen > sch->limit || 4318c2ecf20Sopenharmony_ci q->memory_usage > q->memory_limit) { 4328c2ecf20Sopenharmony_ci struct sk_buff *skb = fq_codel_dequeue(sch); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci q->cstats.drop_len += qdisc_pkt_len(skb); 4358c2ecf20Sopenharmony_ci rtnl_kfree_skbs(skb, skb); 4368c2ecf20Sopenharmony_ci q->cstats.drop_count++; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len); 4398c2ecf20Sopenharmony_ci q->cstats.drop_count = 0; 4408c2ecf20Sopenharmony_ci q->cstats.drop_len = 0; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void fq_codel_destroy(struct Qdisc *sch) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci tcf_block_put(q->block); 4518c2ecf20Sopenharmony_ci kvfree(q->backlogs); 4528c2ecf20Sopenharmony_ci kvfree(q->flows); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic int fq_codel_init(struct Qdisc *sch, struct nlattr *opt, 4568c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 4598c2ecf20Sopenharmony_ci int i; 4608c2ecf20Sopenharmony_ci int err; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci sch->limit = 10*1024; 4638c2ecf20Sopenharmony_ci q->flows_cnt = 1024; 4648c2ecf20Sopenharmony_ci q->memory_limit = 32 << 20; /* 32 MBytes */ 4658c2ecf20Sopenharmony_ci q->drop_batch_size = 64; 4668c2ecf20Sopenharmony_ci q->quantum = psched_mtu(qdisc_dev(sch)); 4678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&q->new_flows); 4688c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&q->old_flows); 4698c2ecf20Sopenharmony_ci codel_params_init(&q->cparams); 4708c2ecf20Sopenharmony_ci codel_stats_init(&q->cstats); 4718c2ecf20Sopenharmony_ci q->cparams.ecn = true; 4728c2ecf20Sopenharmony_ci q->cparams.mtu = psched_mtu(qdisc_dev(sch)); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (opt) { 4758c2ecf20Sopenharmony_ci err = fq_codel_change(sch, opt, extack); 4768c2ecf20Sopenharmony_ci if (err) 4778c2ecf20Sopenharmony_ci goto init_failure; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci err = tcf_block_get(&q->block, &q->filter_list, sch, extack); 4818c2ecf20Sopenharmony_ci if (err) 4828c2ecf20Sopenharmony_ci goto init_failure; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (!q->flows) { 4858c2ecf20Sopenharmony_ci q->flows = kvcalloc(q->flows_cnt, 4868c2ecf20Sopenharmony_ci sizeof(struct fq_codel_flow), 4878c2ecf20Sopenharmony_ci GFP_KERNEL); 4888c2ecf20Sopenharmony_ci if (!q->flows) { 4898c2ecf20Sopenharmony_ci err = -ENOMEM; 4908c2ecf20Sopenharmony_ci goto init_failure; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci q->backlogs = kvcalloc(q->flows_cnt, sizeof(u32), GFP_KERNEL); 4938c2ecf20Sopenharmony_ci if (!q->backlogs) { 4948c2ecf20Sopenharmony_ci err = -ENOMEM; 4958c2ecf20Sopenharmony_ci goto alloc_failure; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci for (i = 0; i < q->flows_cnt; i++) { 4988c2ecf20Sopenharmony_ci struct fq_codel_flow *flow = q->flows + i; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&flow->flowchain); 5018c2ecf20Sopenharmony_ci codel_vars_init(&flow->cvars); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci if (sch->limit >= 1) 5058c2ecf20Sopenharmony_ci sch->flags |= TCQ_F_CAN_BYPASS; 5068c2ecf20Sopenharmony_ci else 5078c2ecf20Sopenharmony_ci sch->flags &= ~TCQ_F_CAN_BYPASS; 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cialloc_failure: 5118c2ecf20Sopenharmony_ci kvfree(q->flows); 5128c2ecf20Sopenharmony_ci q->flows = NULL; 5138c2ecf20Sopenharmony_ciinit_failure: 5148c2ecf20Sopenharmony_ci q->flows_cnt = 0; 5158c2ecf20Sopenharmony_ci return err; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int fq_codel_dump(struct Qdisc *sch, struct sk_buff *skb) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 5218c2ecf20Sopenharmony_ci struct nlattr *opts; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci opts = nla_nest_start_noflag(skb, TCA_OPTIONS); 5248c2ecf20Sopenharmony_ci if (opts == NULL) 5258c2ecf20Sopenharmony_ci goto nla_put_failure; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (nla_put_u32(skb, TCA_FQ_CODEL_TARGET, 5288c2ecf20Sopenharmony_ci codel_time_to_us(q->cparams.target)) || 5298c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_FQ_CODEL_LIMIT, 5308c2ecf20Sopenharmony_ci sch->limit) || 5318c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_FQ_CODEL_INTERVAL, 5328c2ecf20Sopenharmony_ci codel_time_to_us(q->cparams.interval)) || 5338c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_FQ_CODEL_ECN, 5348c2ecf20Sopenharmony_ci q->cparams.ecn) || 5358c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_FQ_CODEL_QUANTUM, 5368c2ecf20Sopenharmony_ci q->quantum) || 5378c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_FQ_CODEL_DROP_BATCH_SIZE, 5388c2ecf20Sopenharmony_ci q->drop_batch_size) || 5398c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_FQ_CODEL_MEMORY_LIMIT, 5408c2ecf20Sopenharmony_ci q->memory_limit) || 5418c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_FQ_CODEL_FLOWS, 5428c2ecf20Sopenharmony_ci q->flows_cnt)) 5438c2ecf20Sopenharmony_ci goto nla_put_failure; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (q->cparams.ce_threshold != CODEL_DISABLED_THRESHOLD && 5468c2ecf20Sopenharmony_ci nla_put_u32(skb, TCA_FQ_CODEL_CE_THRESHOLD, 5478c2ecf20Sopenharmony_ci codel_time_to_us(q->cparams.ce_threshold))) 5488c2ecf20Sopenharmony_ci goto nla_put_failure; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return nla_nest_end(skb, opts); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cinla_put_failure: 5538c2ecf20Sopenharmony_ci return -1; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 5598c2ecf20Sopenharmony_ci struct tc_fq_codel_xstats st = { 5608c2ecf20Sopenharmony_ci .type = TCA_FQ_CODEL_XSTATS_QDISC, 5618c2ecf20Sopenharmony_ci }; 5628c2ecf20Sopenharmony_ci struct list_head *pos; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci st.qdisc_stats.maxpacket = q->cstats.maxpacket; 5658c2ecf20Sopenharmony_ci st.qdisc_stats.drop_overlimit = q->drop_overlimit; 5668c2ecf20Sopenharmony_ci st.qdisc_stats.ecn_mark = q->cstats.ecn_mark; 5678c2ecf20Sopenharmony_ci st.qdisc_stats.new_flow_count = q->new_flow_count; 5688c2ecf20Sopenharmony_ci st.qdisc_stats.ce_mark = q->cstats.ce_mark; 5698c2ecf20Sopenharmony_ci st.qdisc_stats.memory_usage = q->memory_usage; 5708c2ecf20Sopenharmony_ci st.qdisc_stats.drop_overmemory = q->drop_overmemory; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci sch_tree_lock(sch); 5738c2ecf20Sopenharmony_ci list_for_each(pos, &q->new_flows) 5748c2ecf20Sopenharmony_ci st.qdisc_stats.new_flows_len++; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci list_for_each(pos, &q->old_flows) 5778c2ecf20Sopenharmony_ci st.qdisc_stats.old_flows_len++; 5788c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return gnet_stats_copy_app(d, &st, sizeof(st)); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic struct Qdisc *fq_codel_leaf(struct Qdisc *sch, unsigned long arg) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci return NULL; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic unsigned long fq_codel_find(struct Qdisc *sch, u32 classid) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic unsigned long fq_codel_bind(struct Qdisc *sch, unsigned long parent, 5948c2ecf20Sopenharmony_ci u32 classid) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic void fq_codel_unbind(struct Qdisc *q, unsigned long cl) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic struct tcf_block *fq_codel_tcf_block(struct Qdisc *sch, unsigned long cl, 6048c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (cl) 6098c2ecf20Sopenharmony_ci return NULL; 6108c2ecf20Sopenharmony_ci return q->block; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic int fq_codel_dump_class(struct Qdisc *sch, unsigned long cl, 6148c2ecf20Sopenharmony_ci struct sk_buff *skb, struct tcmsg *tcm) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci tcm->tcm_handle |= TC_H_MIN(cl); 6178c2ecf20Sopenharmony_ci return 0; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl, 6218c2ecf20Sopenharmony_ci struct gnet_dump *d) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 6248c2ecf20Sopenharmony_ci u32 idx = cl - 1; 6258c2ecf20Sopenharmony_ci struct gnet_stats_queue qs = { 0 }; 6268c2ecf20Sopenharmony_ci struct tc_fq_codel_xstats xstats; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (idx < q->flows_cnt) { 6298c2ecf20Sopenharmony_ci const struct fq_codel_flow *flow = &q->flows[idx]; 6308c2ecf20Sopenharmony_ci const struct sk_buff *skb; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci memset(&xstats, 0, sizeof(xstats)); 6338c2ecf20Sopenharmony_ci xstats.type = TCA_FQ_CODEL_XSTATS_CLASS; 6348c2ecf20Sopenharmony_ci xstats.class_stats.deficit = flow->deficit; 6358c2ecf20Sopenharmony_ci xstats.class_stats.ldelay = 6368c2ecf20Sopenharmony_ci codel_time_to_us(flow->cvars.ldelay); 6378c2ecf20Sopenharmony_ci xstats.class_stats.count = flow->cvars.count; 6388c2ecf20Sopenharmony_ci xstats.class_stats.lastcount = flow->cvars.lastcount; 6398c2ecf20Sopenharmony_ci xstats.class_stats.dropping = flow->cvars.dropping; 6408c2ecf20Sopenharmony_ci if (flow->cvars.dropping) { 6418c2ecf20Sopenharmony_ci codel_tdiff_t delta = flow->cvars.drop_next - 6428c2ecf20Sopenharmony_ci codel_get_time(); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci xstats.class_stats.drop_next = (delta >= 0) ? 6458c2ecf20Sopenharmony_ci codel_time_to_us(delta) : 6468c2ecf20Sopenharmony_ci -codel_time_to_us(-delta); 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci if (flow->head) { 6498c2ecf20Sopenharmony_ci sch_tree_lock(sch); 6508c2ecf20Sopenharmony_ci skb = flow->head; 6518c2ecf20Sopenharmony_ci while (skb) { 6528c2ecf20Sopenharmony_ci qs.qlen++; 6538c2ecf20Sopenharmony_ci skb = skb->next; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci sch_tree_unlock(sch); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci qs.backlog = q->backlogs[idx]; 6588c2ecf20Sopenharmony_ci qs.drops = 0; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0) 6618c2ecf20Sopenharmony_ci return -1; 6628c2ecf20Sopenharmony_ci if (idx < q->flows_cnt) 6638c2ecf20Sopenharmony_ci return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); 6648c2ecf20Sopenharmony_ci return 0; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic void fq_codel_walk(struct Qdisc *sch, struct qdisc_walker *arg) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct fq_codel_sched_data *q = qdisc_priv(sch); 6708c2ecf20Sopenharmony_ci unsigned int i; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (arg->stop) 6738c2ecf20Sopenharmony_ci return; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci for (i = 0; i < q->flows_cnt; i++) { 6768c2ecf20Sopenharmony_ci if (list_empty(&q->flows[i].flowchain) || 6778c2ecf20Sopenharmony_ci arg->count < arg->skip) { 6788c2ecf20Sopenharmony_ci arg->count++; 6798c2ecf20Sopenharmony_ci continue; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci if (arg->fn(sch, i + 1, arg) < 0) { 6828c2ecf20Sopenharmony_ci arg->stop = 1; 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci arg->count++; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic const struct Qdisc_class_ops fq_codel_class_ops = { 6908c2ecf20Sopenharmony_ci .leaf = fq_codel_leaf, 6918c2ecf20Sopenharmony_ci .find = fq_codel_find, 6928c2ecf20Sopenharmony_ci .tcf_block = fq_codel_tcf_block, 6938c2ecf20Sopenharmony_ci .bind_tcf = fq_codel_bind, 6948c2ecf20Sopenharmony_ci .unbind_tcf = fq_codel_unbind, 6958c2ecf20Sopenharmony_ci .dump = fq_codel_dump_class, 6968c2ecf20Sopenharmony_ci .dump_stats = fq_codel_dump_class_stats, 6978c2ecf20Sopenharmony_ci .walk = fq_codel_walk, 6988c2ecf20Sopenharmony_ci}; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = { 7018c2ecf20Sopenharmony_ci .cl_ops = &fq_codel_class_ops, 7028c2ecf20Sopenharmony_ci .id = "fq_codel", 7038c2ecf20Sopenharmony_ci .priv_size = sizeof(struct fq_codel_sched_data), 7048c2ecf20Sopenharmony_ci .enqueue = fq_codel_enqueue, 7058c2ecf20Sopenharmony_ci .dequeue = fq_codel_dequeue, 7068c2ecf20Sopenharmony_ci .peek = qdisc_peek_dequeued, 7078c2ecf20Sopenharmony_ci .init = fq_codel_init, 7088c2ecf20Sopenharmony_ci .reset = fq_codel_reset, 7098c2ecf20Sopenharmony_ci .destroy = fq_codel_destroy, 7108c2ecf20Sopenharmony_ci .change = fq_codel_change, 7118c2ecf20Sopenharmony_ci .dump = fq_codel_dump, 7128c2ecf20Sopenharmony_ci .dump_stats = fq_codel_dump_stats, 7138c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7148c2ecf20Sopenharmony_ci}; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int __init fq_codel_module_init(void) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci return register_qdisc(&fq_codel_qdisc_ops); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic void __exit fq_codel_module_exit(void) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci unregister_qdisc(&fq_codel_qdisc_ops); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cimodule_init(fq_codel_module_init) 7278c2ecf20Sopenharmony_cimodule_exit(fq_codel_module_exit) 7288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eric Dumazet"); 7298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Fair Queue CoDel discipline"); 731