forked from mirrors/linux
		
	net: sched: add Flow Queue PIE packet scheduler
Principles:
  - Packets are classified on flows.
  - This is a Stochastic model (as we use a hash, several flows might
                                be hashed to the same slot)
  - Each flow has a PIE managed queue.
  - Flows are linked onto two (Round Robin) lists,
    so that new flows have priority on old ones.
  - For a given flow, packets are not reordered.
  - Drops during enqueue only.
  - ECN capability is off by default.
  - ECN threshold (if ECN is enabled) is at 10% by default.
  - Uses timestamps to calculate queue delay by default.
Usage:
tc qdisc ... fq_pie [ limit PACKETS ] [ flows NUMBER ]
                    [ target TIME ] [ tupdate TIME ]
                    [ alpha NUMBER ] [ beta NUMBER ]
                    [ quantum BYTES ] [ memory_limit BYTES ]
                    [ ecnprob PERCENTAGE ] [ [no]ecn ]
                    [ [no]bytemode ] [ [no_]dq_rate_estimator ]
defaults:
  limit: 10240 packets, flows: 1024
  target: 15 ms, tupdate: 15 ms (in jiffies)
  alpha: 1/8, beta : 5/4
  quantum: device MTU, memory_limit: 32 Mb
  ecnprob: 10%, ecn: off
  bytemode: off, dq_rate_estimator: off
Signed-off-by: Mohit P. Tahiliani <tahiliani@nitk.edu.in>
Signed-off-by: Sachin D. Patil <sdp.sachin@gmail.com>
Signed-off-by: V. Saicharan <vsaicharan1998@gmail.com>
Signed-off-by: Mohit Bhasi <mohitbhasi1998@gmail.com>
Signed-off-by: Leslie Monis <lesliemonis@gmail.com>
Signed-off-by: Gautam Ramakrishnan <gautamramk@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									5205ea00cd
								
							
						
					
					
						commit
						ec97ecf1eb
					
				
					 5 changed files with 609 additions and 0 deletions
				
			
		|  | @ -81,9 +81,11 @@ struct pie_stats { | |||
| /**
 | ||||
|  * struct pie_skb_cb - contains private skb vars | ||||
|  * @enqueue_time:	timestamp when the packet is enqueued | ||||
|  * @mem_usage:		size of the skb during enqueue | ||||
|  */ | ||||
| struct pie_skb_cb { | ||||
| 	psched_time_t enqueue_time; | ||||
| 	u32 mem_usage; | ||||
| }; | ||||
| 
 | ||||
| static inline void pie_params_init(struct pie_params *params) | ||||
|  |  | |||
|  | @ -971,6 +971,37 @@ struct tc_pie_xstats { | |||
| 	__u32 ecn_mark;			/* packets marked with ecn*/ | ||||
| }; | ||||
| 
 | ||||
| /* FQ PIE */ | ||||
| enum { | ||||
| 	TCA_FQ_PIE_UNSPEC, | ||||
| 	TCA_FQ_PIE_LIMIT, | ||||
| 	TCA_FQ_PIE_FLOWS, | ||||
| 	TCA_FQ_PIE_TARGET, | ||||
| 	TCA_FQ_PIE_TUPDATE, | ||||
| 	TCA_FQ_PIE_ALPHA, | ||||
| 	TCA_FQ_PIE_BETA, | ||||
| 	TCA_FQ_PIE_QUANTUM, | ||||
| 	TCA_FQ_PIE_MEMORY_LIMIT, | ||||
| 	TCA_FQ_PIE_ECN_PROB, | ||||
| 	TCA_FQ_PIE_ECN, | ||||
| 	TCA_FQ_PIE_BYTEMODE, | ||||
| 	TCA_FQ_PIE_DQ_RATE_ESTIMATOR, | ||||
| 	__TCA_FQ_PIE_MAX | ||||
| }; | ||||
| #define TCA_FQ_PIE_MAX   (__TCA_FQ_PIE_MAX - 1) | ||||
| 
 | ||||
| struct tc_fq_pie_xstats { | ||||
| 	__u32 packets_in;	/* total number of packets enqueued */ | ||||
| 	__u32 dropped;		/* packets dropped due to fq_pie_action */ | ||||
| 	__u32 overlimit;	/* dropped due to lack of space in queue */ | ||||
| 	__u32 overmemory;	/* dropped due to lack of memory in queue */ | ||||
| 	__u32 ecn_mark;		/* packets marked with ecn */ | ||||
| 	__u32 new_flow_count;	/* count of new flows created by packets */ | ||||
| 	__u32 new_flows_len;	/* count of flows in new list */ | ||||
| 	__u32 old_flows_len;	/* count of flows in old list */ | ||||
| 	__u32 memory_usage;	/* total memory across all queues */ | ||||
| }; | ||||
| 
 | ||||
| /* CBS */ | ||||
| struct tc_cbs_qopt { | ||||
| 	__u8 offload; | ||||
|  |  | |||
|  | @ -366,6 +366,19 @@ config NET_SCH_PIE | |||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config NET_SCH_FQ_PIE | ||||
| 	depends on NET_SCH_PIE | ||||
| 	tristate "Flow Queue Proportional Integral controller Enhanced (FQ-PIE)" | ||||
| 	help | ||||
| 	  Say Y here if you want to use the Flow Queue Proportional Integral | ||||
| 	  controller Enhanced (FQ-PIE) packet scheduling algorithm. | ||||
| 	  For more information, please see https://tools.ietf.org/html/rfc8033 | ||||
| 
 | ||||
| 	  To compile this driver as a module, choose M here: the module | ||||
| 	  will be called sch_fq_pie. | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config NET_SCH_INGRESS | ||||
| 	tristate "Ingress/classifier-action Qdisc" | ||||
| 	depends on NET_CLS_ACT | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ obj-$(CONFIG_NET_SCH_CAKE)	+= sch_cake.o | |||
| obj-$(CONFIG_NET_SCH_FQ)	+= sch_fq.o | ||||
| obj-$(CONFIG_NET_SCH_HHF)	+= sch_hhf.o | ||||
| obj-$(CONFIG_NET_SCH_PIE)	+= sch_pie.o | ||||
| obj-$(CONFIG_NET_SCH_FQ_PIE)	+= sch_fq_pie.o | ||||
| obj-$(CONFIG_NET_SCH_CBS)	+= sch_cbs.o | ||||
| obj-$(CONFIG_NET_SCH_ETF)	+= sch_etf.o | ||||
| obj-$(CONFIG_NET_SCH_TAPRIO)	+= sch_taprio.o | ||||
|  |  | |||
							
								
								
									
										562
									
								
								net/sched/sch_fq_pie.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										562
									
								
								net/sched/sch_fq_pie.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,562 @@ | |||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /* Flow Queue PIE discipline
 | ||||
|  * | ||||
|  * Copyright (C) 2019 Mohit P. Tahiliani <tahiliani@nitk.edu.in> | ||||
|  * Copyright (C) 2019 Sachin D. Patil <sdp.sachin@gmail.com> | ||||
|  * Copyright (C) 2019 V. Saicharan <vsaicharan1998@gmail.com> | ||||
|  * Copyright (C) 2019 Mohit Bhasi <mohitbhasi1998@gmail.com> | ||||
|  * Copyright (C) 2019 Leslie Monis <lesliemonis@gmail.com> | ||||
|  * Copyright (C) 2019 Gautam Ramakrishnan <gautamramk@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/jhash.h> | ||||
| #include <linux/sizes.h> | ||||
| #include <linux/vmalloc.h> | ||||
| #include <net/pkt_cls.h> | ||||
| #include <net/pie.h> | ||||
| 
 | ||||
| /* Flow Queue PIE
 | ||||
|  * | ||||
|  * Principles: | ||||
|  *   - Packets are classified on flows. | ||||
|  *   - This is a Stochastic model (as we use a hash, several flows might | ||||
|  *                                 be hashed to the same slot) | ||||
|  *   - Each flow has a PIE managed queue. | ||||
|  *   - Flows are linked onto two (Round Robin) lists, | ||||
|  *     so that new flows have priority on old ones. | ||||
|  *   - For a given flow, packets are not reordered. | ||||
|  *   - Drops during enqueue only. | ||||
|  *   - ECN capability is off by default. | ||||
|  *   - ECN threshold (if ECN is enabled) is at 10% by default. | ||||
|  *   - Uses timestamps to calculate queue delay by default. | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * struct fq_pie_flow - contains data for each flow | ||||
|  * @vars:	pie vars associated with the flow | ||||
|  * @deficit:	number of remaining byte credits | ||||
|  * @backlog:	size of data in the flow | ||||
|  * @qlen:	number of packets in the flow | ||||
|  * @flowchain:	flowchain for the flow | ||||
|  * @head:	first packet in the flow | ||||
|  * @tail:	last packet in the flow | ||||
|  */ | ||||
| struct fq_pie_flow { | ||||
| 	struct pie_vars vars; | ||||
| 	s32 deficit; | ||||
| 	u32 backlog; | ||||
| 	u32 qlen; | ||||
| 	struct list_head flowchain; | ||||
| 	struct sk_buff *head; | ||||
| 	struct sk_buff *tail; | ||||
| }; | ||||
| 
 | ||||
| struct fq_pie_sched_data { | ||||
| 	struct tcf_proto __rcu *filter_list; /* optional external classifier */ | ||||
| 	struct tcf_block *block; | ||||
| 	struct fq_pie_flow *flows; | ||||
| 	struct Qdisc *sch; | ||||
| 	struct list_head old_flows; | ||||
| 	struct list_head new_flows; | ||||
| 	struct pie_params p_params; | ||||
| 	u32 ecn_prob; | ||||
| 	u32 flows_cnt; | ||||
| 	u32 quantum; | ||||
| 	u32 memory_limit; | ||||
| 	u32 new_flow_count; | ||||
| 	u32 memory_usage; | ||||
| 	u32 overmemory; | ||||
| 	struct pie_stats stats; | ||||
| 	struct timer_list adapt_timer; | ||||
| }; | ||||
| 
 | ||||
| static unsigned int fq_pie_hash(const struct fq_pie_sched_data *q, | ||||
| 				struct sk_buff *skb) | ||||
| { | ||||
| 	return reciprocal_scale(skb_get_hash(skb), q->flows_cnt); | ||||
| } | ||||
| 
 | ||||
| static unsigned int fq_pie_classify(struct sk_buff *skb, struct Qdisc *sch, | ||||
| 				    int *qerr) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 	struct tcf_proto *filter; | ||||
| 	struct tcf_result res; | ||||
| 	int result; | ||||
| 
 | ||||
| 	if (TC_H_MAJ(skb->priority) == sch->handle && | ||||
| 	    TC_H_MIN(skb->priority) > 0 && | ||||
| 	    TC_H_MIN(skb->priority) <= q->flows_cnt) | ||||
| 		return TC_H_MIN(skb->priority); | ||||
| 
 | ||||
| 	filter = rcu_dereference_bh(q->filter_list); | ||||
| 	if (!filter) | ||||
| 		return fq_pie_hash(q, skb) + 1; | ||||
| 
 | ||||
| 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; | ||||
| 	result = tcf_classify(skb, filter, &res, false); | ||||
| 	if (result >= 0) { | ||||
| #ifdef CONFIG_NET_CLS_ACT | ||||
| 		switch (result) { | ||||
| 		case TC_ACT_STOLEN: | ||||
| 		case TC_ACT_QUEUED: | ||||
| 		case TC_ACT_TRAP: | ||||
| 			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; | ||||
| 			/* fall through */ | ||||
| 		case TC_ACT_SHOT: | ||||
| 			return 0; | ||||
| 		} | ||||
| #endif | ||||
| 		if (TC_H_MIN(res.classid) <= q->flows_cnt) | ||||
| 			return TC_H_MIN(res.classid); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* add skb to flow queue (tail add) */ | ||||
| static inline void flow_queue_add(struct fq_pie_flow *flow, | ||||
| 				  struct sk_buff *skb) | ||||
| { | ||||
| 	if (!flow->head) | ||||
| 		flow->head = skb; | ||||
| 	else | ||||
| 		flow->tail->next = skb; | ||||
| 	flow->tail = skb; | ||||
| 	skb->next = NULL; | ||||
| } | ||||
| 
 | ||||
| static int fq_pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, | ||||
| 				struct sk_buff **to_free) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 	struct fq_pie_flow *sel_flow; | ||||
| 	int uninitialized_var(ret); | ||||
| 	u8 memory_limited = false; | ||||
| 	u8 enqueue = false; | ||||
| 	u32 pkt_len; | ||||
| 	u32 idx; | ||||
| 
 | ||||
| 	/* Classifies packet into corresponding flow */ | ||||
| 	idx = fq_pie_classify(skb, sch, &ret); | ||||
| 	sel_flow = &q->flows[idx]; | ||||
| 
 | ||||
| 	/* Checks whether adding a new packet would exceed memory limit */ | ||||
| 	get_pie_cb(skb)->mem_usage = skb->truesize; | ||||
| 	memory_limited = q->memory_usage > q->memory_limit + skb->truesize; | ||||
| 
 | ||||
| 	/* Checks if the qdisc is full */ | ||||
| 	if (unlikely(qdisc_qlen(sch) >= sch->limit)) { | ||||
| 		q->stats.overlimit++; | ||||
| 		goto out; | ||||
| 	} else if (unlikely(memory_limited)) { | ||||
| 		q->overmemory++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!pie_drop_early(sch, &q->p_params, &sel_flow->vars, | ||||
| 			    sel_flow->backlog, skb->len)) { | ||||
| 		enqueue = true; | ||||
| 	} else if (q->p_params.ecn && | ||||
| 		   sel_flow->vars.prob <= (MAX_PROB / 100) * q->ecn_prob && | ||||
| 		   INET_ECN_set_ce(skb)) { | ||||
| 		/* If packet is ecn capable, mark it if drop probability
 | ||||
| 		 * is lower than the parameter ecn_prob, else drop it. | ||||
| 		 */ | ||||
| 		q->stats.ecn_mark++; | ||||
| 		enqueue = true; | ||||
| 	} | ||||
| 	if (enqueue) { | ||||
| 		/* Set enqueue time only when dq_rate_estimator is disabled. */ | ||||
| 		if (!q->p_params.dq_rate_estimator) | ||||
| 			pie_set_enqueue_time(skb); | ||||
| 
 | ||||
| 		pkt_len = qdisc_pkt_len(skb); | ||||
| 		q->stats.packets_in++; | ||||
| 		q->memory_usage += skb->truesize; | ||||
| 		sch->qstats.backlog += pkt_len; | ||||
| 		sch->q.qlen++; | ||||
| 		flow_queue_add(sel_flow, skb); | ||||
| 		if (list_empty(&sel_flow->flowchain)) { | ||||
| 			list_add_tail(&sel_flow->flowchain, &q->new_flows); | ||||
| 			q->new_flow_count++; | ||||
| 			sel_flow->deficit = q->quantum; | ||||
| 			sel_flow->qlen = 0; | ||||
| 			sel_flow->backlog = 0; | ||||
| 		} | ||||
| 		sel_flow->qlen++; | ||||
| 		sel_flow->backlog += pkt_len; | ||||
| 		return NET_XMIT_SUCCESS; | ||||
| 	} | ||||
| out: | ||||
| 	q->stats.dropped++; | ||||
| 	sel_flow->vars.accu_prob = 0; | ||||
| 	sel_flow->vars.accu_prob_overflows = 0; | ||||
| 	__qdisc_drop(skb, to_free); | ||||
| 	qdisc_qstats_drop(sch); | ||||
| 	return NET_XMIT_CN; | ||||
| } | ||||
| 
 | ||||
| static const struct nla_policy fq_pie_policy[TCA_FQ_PIE_MAX + 1] = { | ||||
| 	[TCA_FQ_PIE_LIMIT]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_FLOWS]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_TARGET]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_TUPDATE]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_ALPHA]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_BETA]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_QUANTUM]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_MEMORY_LIMIT]	= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_ECN_PROB]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_ECN]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_BYTEMODE]		= {.type = NLA_U32}, | ||||
| 	[TCA_FQ_PIE_DQ_RATE_ESTIMATOR]	= {.type = NLA_U32}, | ||||
| }; | ||||
| 
 | ||||
| static inline struct sk_buff *dequeue_head(struct fq_pie_flow *flow) | ||||
| { | ||||
| 	struct sk_buff *skb = flow->head; | ||||
| 
 | ||||
| 	flow->head = skb->next; | ||||
| 	skb->next = NULL; | ||||
| 	return skb; | ||||
| } | ||||
| 
 | ||||
| static struct sk_buff *fq_pie_qdisc_dequeue(struct Qdisc *sch) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 	struct sk_buff *skb = NULL; | ||||
| 	struct fq_pie_flow *flow; | ||||
| 	struct list_head *head; | ||||
| 	u32 pkt_len; | ||||
| 
 | ||||
| begin: | ||||
| 	head = &q->new_flows; | ||||
| 	if (list_empty(head)) { | ||||
| 		head = &q->old_flows; | ||||
| 		if (list_empty(head)) | ||||
| 			return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	flow = list_first_entry(head, struct fq_pie_flow, flowchain); | ||||
| 	/* Flow has exhausted all its credits */ | ||||
| 	if (flow->deficit <= 0) { | ||||
| 		flow->deficit += q->quantum; | ||||
| 		list_move_tail(&flow->flowchain, &q->old_flows); | ||||
| 		goto begin; | ||||
| 	} | ||||
| 
 | ||||
| 	if (flow->head) { | ||||
| 		skb = dequeue_head(flow); | ||||
| 		pkt_len = qdisc_pkt_len(skb); | ||||
| 		sch->qstats.backlog -= pkt_len; | ||||
| 		sch->q.qlen--; | ||||
| 		qdisc_bstats_update(sch, skb); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!skb) { | ||||
| 		/* force a pass through old_flows to prevent starvation */ | ||||
| 		if (head == &q->new_flows && !list_empty(&q->old_flows)) | ||||
| 			list_move_tail(&flow->flowchain, &q->old_flows); | ||||
| 		else | ||||
| 			list_del_init(&flow->flowchain); | ||||
| 		goto begin; | ||||
| 	} | ||||
| 
 | ||||
| 	flow->qlen--; | ||||
| 	flow->deficit -= pkt_len; | ||||
| 	flow->backlog -= pkt_len; | ||||
| 	q->memory_usage -= get_pie_cb(skb)->mem_usage; | ||||
| 	pie_process_dequeue(skb, &q->p_params, &flow->vars, flow->backlog); | ||||
| 	return skb; | ||||
| } | ||||
| 
 | ||||
| static int fq_pie_change(struct Qdisc *sch, struct nlattr *opt, | ||||
| 			 struct netlink_ext_ack *extack) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 	struct nlattr *tb[TCA_FQ_PIE_MAX + 1]; | ||||
| 	unsigned int len_dropped = 0; | ||||
| 	unsigned int num_dropped = 0; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!opt) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	err = nla_parse_nested(tb, TCA_FQ_PIE_MAX, opt, fq_pie_policy, extack); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	sch_tree_lock(sch); | ||||
| 	if (tb[TCA_FQ_PIE_LIMIT]) { | ||||
| 		u32 limit = nla_get_u32(tb[TCA_FQ_PIE_LIMIT]); | ||||
| 
 | ||||
| 		q->p_params.limit = limit; | ||||
| 		sch->limit = limit; | ||||
| 	} | ||||
| 	if (tb[TCA_FQ_PIE_FLOWS]) { | ||||
| 		if (q->flows) { | ||||
| 			NL_SET_ERR_MSG_MOD(extack, | ||||
| 					   "Number of flows cannot be changed"); | ||||
| 			goto flow_error; | ||||
| 		} | ||||
| 		q->flows_cnt = nla_get_u32(tb[TCA_FQ_PIE_FLOWS]); | ||||
| 		if (!q->flows_cnt || q->flows_cnt > 65536) { | ||||
| 			NL_SET_ERR_MSG_MOD(extack, | ||||
| 					   "Number of flows must be < 65536"); | ||||
| 			goto flow_error; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* convert from microseconds to pschedtime */ | ||||
| 	if (tb[TCA_FQ_PIE_TARGET]) { | ||||
| 		/* target is in us */ | ||||
| 		u32 target = nla_get_u32(tb[TCA_FQ_PIE_TARGET]); | ||||
| 
 | ||||
| 		/* convert to pschedtime */ | ||||
| 		q->p_params.target = | ||||
| 			PSCHED_NS2TICKS((u64)target * NSEC_PER_USEC); | ||||
| 	} | ||||
| 
 | ||||
| 	/* tupdate is in jiffies */ | ||||
| 	if (tb[TCA_FQ_PIE_TUPDATE]) | ||||
| 		q->p_params.tupdate = | ||||
| 			usecs_to_jiffies(nla_get_u32(tb[TCA_FQ_PIE_TUPDATE])); | ||||
| 
 | ||||
| 	if (tb[TCA_FQ_PIE_ALPHA]) | ||||
| 		q->p_params.alpha = nla_get_u32(tb[TCA_FQ_PIE_ALPHA]); | ||||
| 
 | ||||
| 	if (tb[TCA_FQ_PIE_BETA]) | ||||
| 		q->p_params.beta = nla_get_u32(tb[TCA_FQ_PIE_BETA]); | ||||
| 
 | ||||
| 	if (tb[TCA_FQ_PIE_QUANTUM]) | ||||
| 		q->quantum = nla_get_u32(tb[TCA_FQ_PIE_QUANTUM]); | ||||
| 
 | ||||
| 	if (tb[TCA_FQ_PIE_MEMORY_LIMIT]) | ||||
| 		q->memory_limit = nla_get_u32(tb[TCA_FQ_PIE_MEMORY_LIMIT]); | ||||
| 
 | ||||
| 	if (tb[TCA_FQ_PIE_ECN_PROB]) | ||||
| 		q->ecn_prob = nla_get_u32(tb[TCA_FQ_PIE_ECN_PROB]); | ||||
| 
 | ||||
| 	if (tb[TCA_FQ_PIE_ECN]) | ||||
| 		q->p_params.ecn = nla_get_u32(tb[TCA_FQ_PIE_ECN]); | ||||
| 
 | ||||
| 	if (tb[TCA_FQ_PIE_BYTEMODE]) | ||||
| 		q->p_params.bytemode = nla_get_u32(tb[TCA_FQ_PIE_BYTEMODE]); | ||||
| 
 | ||||
| 	if (tb[TCA_FQ_PIE_DQ_RATE_ESTIMATOR]) | ||||
| 		q->p_params.dq_rate_estimator = | ||||
| 			nla_get_u32(tb[TCA_FQ_PIE_DQ_RATE_ESTIMATOR]); | ||||
| 
 | ||||
| 	/* Drop excess packets if new limit is lower */ | ||||
| 	while (sch->q.qlen > sch->limit) { | ||||
| 		struct sk_buff *skb = fq_pie_qdisc_dequeue(sch); | ||||
| 
 | ||||
| 		kfree_skb(skb); | ||||
| 		len_dropped += qdisc_pkt_len(skb); | ||||
| 		num_dropped += 1; | ||||
| 	} | ||||
| 	qdisc_tree_reduce_backlog(sch, num_dropped, len_dropped); | ||||
| 
 | ||||
| 	sch_tree_unlock(sch); | ||||
| 	return 0; | ||||
| 
 | ||||
| flow_error: | ||||
| 	sch_tree_unlock(sch); | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| static void fq_pie_timer(struct timer_list *t) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = from_timer(q, t, adapt_timer); | ||||
| 	struct Qdisc *sch = q->sch; | ||||
| 	spinlock_t *root_lock; /* to lock qdisc for probability calculations */ | ||||
| 	u16 idx; | ||||
| 
 | ||||
| 	root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | ||||
| 	spin_lock(root_lock); | ||||
| 
 | ||||
| 	for (idx = 0; idx < q->flows_cnt; idx++) | ||||
| 		pie_calculate_probability(&q->p_params, &q->flows[idx].vars, | ||||
| 					  q->flows[idx].backlog); | ||||
| 
 | ||||
| 	/* reset the timer to fire after 'tupdate' jiffies. */ | ||||
| 	if (q->p_params.tupdate) | ||||
| 		mod_timer(&q->adapt_timer, jiffies + q->p_params.tupdate); | ||||
| 
 | ||||
| 	spin_unlock(root_lock); | ||||
| } | ||||
| 
 | ||||
| static int fq_pie_init(struct Qdisc *sch, struct nlattr *opt, | ||||
| 		       struct netlink_ext_ack *extack) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 	int err; | ||||
| 	u16 idx; | ||||
| 
 | ||||
| 	pie_params_init(&q->p_params); | ||||
| 	sch->limit = 10 * 1024; | ||||
| 	q->p_params.limit = sch->limit; | ||||
| 	q->quantum = psched_mtu(qdisc_dev(sch)); | ||||
| 	q->sch = sch; | ||||
| 	q->ecn_prob = 10; | ||||
| 	q->flows_cnt = 1024; | ||||
| 	q->memory_limit = SZ_32M; | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&q->new_flows); | ||||
| 	INIT_LIST_HEAD(&q->old_flows); | ||||
| 
 | ||||
| 	if (opt) { | ||||
| 		err = fq_pie_change(sch, opt, extack); | ||||
| 
 | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 
 | ||||
| 	err = tcf_block_get(&q->block, &q->filter_list, sch, extack); | ||||
| 	if (err) | ||||
| 		goto init_failure; | ||||
| 
 | ||||
| 	q->flows = kvcalloc(q->flows_cnt, sizeof(struct fq_pie_flow), | ||||
| 			    GFP_KERNEL); | ||||
| 	if (!q->flows) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto init_failure; | ||||
| 	} | ||||
| 	for (idx = 0; idx < q->flows_cnt; idx++) { | ||||
| 		struct fq_pie_flow *flow = q->flows + idx; | ||||
| 
 | ||||
| 		INIT_LIST_HEAD(&flow->flowchain); | ||||
| 		pie_vars_init(&flow->vars); | ||||
| 	} | ||||
| 
 | ||||
| 	timer_setup(&q->adapt_timer, fq_pie_timer, 0); | ||||
| 	mod_timer(&q->adapt_timer, jiffies + HZ / 2); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| init_failure: | ||||
| 	q->flows_cnt = 0; | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int fq_pie_dump(struct Qdisc *sch, struct sk_buff *skb) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 	struct nlattr *opts; | ||||
| 
 | ||||
| 	opts = nla_nest_start(skb, TCA_OPTIONS); | ||||
| 	if (!opts) | ||||
| 		return -EMSGSIZE; | ||||
| 
 | ||||
| 	/* convert target from pschedtime to us */ | ||||
| 	if (nla_put_u32(skb, TCA_FQ_PIE_LIMIT, sch->limit) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_FLOWS, q->flows_cnt) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_TARGET, | ||||
| 			((u32)PSCHED_TICKS2NS(q->p_params.target)) / | ||||
| 			NSEC_PER_USEC) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_TUPDATE, | ||||
| 			jiffies_to_usecs(q->p_params.tupdate)) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_ALPHA, q->p_params.alpha) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_BETA, q->p_params.beta) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_QUANTUM, q->quantum) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_MEMORY_LIMIT, q->memory_limit) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_ECN_PROB, q->ecn_prob) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_ECN, q->p_params.ecn) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_BYTEMODE, q->p_params.bytemode) || | ||||
| 	    nla_put_u32(skb, TCA_FQ_PIE_DQ_RATE_ESTIMATOR, | ||||
| 			q->p_params.dq_rate_estimator)) | ||||
| 		goto nla_put_failure; | ||||
| 
 | ||||
| 	return nla_nest_end(skb, opts); | ||||
| 
 | ||||
| nla_put_failure: | ||||
| 	nla_nest_cancel(skb, opts); | ||||
| 	return -EMSGSIZE; | ||||
| } | ||||
| 
 | ||||
| static int fq_pie_dump_stats(struct Qdisc *sch, struct gnet_dump *d) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 	struct tc_fq_pie_xstats st = { | ||||
| 		.packets_in	= q->stats.packets_in, | ||||
| 		.overlimit	= q->stats.overlimit, | ||||
| 		.overmemory	= q->overmemory, | ||||
| 		.dropped	= q->stats.dropped, | ||||
| 		.ecn_mark	= q->stats.ecn_mark, | ||||
| 		.new_flow_count = q->new_flow_count, | ||||
| 		.memory_usage   = q->memory_usage, | ||||
| 	}; | ||||
| 	struct list_head *pos; | ||||
| 
 | ||||
| 	sch_tree_lock(sch); | ||||
| 	list_for_each(pos, &q->new_flows) | ||||
| 		st.new_flows_len++; | ||||
| 
 | ||||
| 	list_for_each(pos, &q->old_flows) | ||||
| 		st.old_flows_len++; | ||||
| 	sch_tree_unlock(sch); | ||||
| 
 | ||||
| 	return gnet_stats_copy_app(d, &st, sizeof(st)); | ||||
| } | ||||
| 
 | ||||
| static void fq_pie_reset(struct Qdisc *sch) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 	u16 idx; | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&q->new_flows); | ||||
| 	INIT_LIST_HEAD(&q->old_flows); | ||||
| 	for (idx = 0; idx < q->flows_cnt; idx++) { | ||||
| 		struct fq_pie_flow *flow = q->flows + idx; | ||||
| 
 | ||||
| 		/* Removes all packets from flow */ | ||||
| 		rtnl_kfree_skbs(flow->head, flow->tail); | ||||
| 		flow->head = NULL; | ||||
| 
 | ||||
| 		INIT_LIST_HEAD(&flow->flowchain); | ||||
| 		pie_vars_init(&flow->vars); | ||||
| 	} | ||||
| 
 | ||||
| 	sch->q.qlen = 0; | ||||
| 	sch->qstats.backlog = 0; | ||||
| } | ||||
| 
 | ||||
| static void fq_pie_destroy(struct Qdisc *sch) | ||||
| { | ||||
| 	struct fq_pie_sched_data *q = qdisc_priv(sch); | ||||
| 
 | ||||
| 	tcf_block_put(q->block); | ||||
| 	del_timer_sync(&q->adapt_timer); | ||||
| 	kvfree(q->flows); | ||||
| } | ||||
| 
 | ||||
| static struct Qdisc_ops fq_pie_qdisc_ops __read_mostly = { | ||||
| 	.id		= "fq_pie", | ||||
| 	.priv_size	= sizeof(struct fq_pie_sched_data), | ||||
| 	.enqueue	= fq_pie_qdisc_enqueue, | ||||
| 	.dequeue	= fq_pie_qdisc_dequeue, | ||||
| 	.peek		= qdisc_peek_dequeued, | ||||
| 	.init		= fq_pie_init, | ||||
| 	.destroy	= fq_pie_destroy, | ||||
| 	.reset		= fq_pie_reset, | ||||
| 	.change		= fq_pie_change, | ||||
| 	.dump		= fq_pie_dump, | ||||
| 	.dump_stats	= fq_pie_dump_stats, | ||||
| 	.owner		= THIS_MODULE, | ||||
| }; | ||||
| 
 | ||||
| static int __init fq_pie_module_init(void) | ||||
| { | ||||
| 	return register_qdisc(&fq_pie_qdisc_ops); | ||||
| } | ||||
| 
 | ||||
| static void __exit fq_pie_module_exit(void) | ||||
| { | ||||
| 	unregister_qdisc(&fq_pie_qdisc_ops); | ||||
| } | ||||
| 
 | ||||
| module_init(fq_pie_module_init); | ||||
| module_exit(fq_pie_module_exit); | ||||
| 
 | ||||
| MODULE_DESCRIPTION("Flow Queue Proportional Integral controller Enhanced (FQ-PIE)"); | ||||
| MODULE_AUTHOR("Mohit P. Tahiliani"); | ||||
| MODULE_LICENSE("GPL"); | ||||
		Loading…
	
		Reference in a new issue
	
	 Mohit P. Tahiliani
						Mohit P. Tahiliani