162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2003 Patrick McHardy, <kaber@trash.net>
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or
562306a36Sopenharmony_ci * modify it under the terms of the GNU General Public License
662306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 2
762306a36Sopenharmony_ci * of the License, or (at your option) any later version.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * 2003-10-17 - Ported from altq
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software and
1562306a36Sopenharmony_ci * its documentation is hereby granted (including for commercial or
1662306a36Sopenharmony_ci * for-profit use), provided that both the copyright notice and this
1762306a36Sopenharmony_ci * permission notice appear in all copies of the software, derivative
1862306a36Sopenharmony_ci * works, or modified versions, and any portions thereof.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * THIS SOFTWARE IS EXPERIMENTAL AND IS KNOWN TO HAVE BUGS, SOME OF
2162306a36Sopenharmony_ci * WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON PROVIDES THIS
2262306a36Sopenharmony_ci * SOFTWARE IN ITS ``AS IS'' CONDITION, AND ANY EXPRESS OR IMPLIED
2362306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2462306a36Sopenharmony_ci * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2562306a36Sopenharmony_ci * DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
2662306a36Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2762306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
2862306a36Sopenharmony_ci * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2962306a36Sopenharmony_ci * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3062306a36Sopenharmony_ci * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3162306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3262306a36Sopenharmony_ci * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
3362306a36Sopenharmony_ci * DAMAGE.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Carnegie Mellon encourages (but does not require) users of this
3662306a36Sopenharmony_ci * software to return any improvements or extensions that they make,
3762306a36Sopenharmony_ci * and to grant Carnegie Mellon the rights to redistribute these
3862306a36Sopenharmony_ci * changes without encumbrance.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * H-FSC is described in Proceedings of SIGCOMM'97,
4262306a36Sopenharmony_ci * "A Hierarchical Fair Service Curve Algorithm for Link-Sharing,
4362306a36Sopenharmony_ci * Real-Time and Priority Service"
4462306a36Sopenharmony_ci * by Ion Stoica, Hui Zhang, and T. S. Eugene Ng.
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * Oleg Cherevko <olwi@aq.ml.com.ua> added the upperlimit for link-sharing.
4762306a36Sopenharmony_ci * when a class has an upperlimit, the fit-time is computed from the
4862306a36Sopenharmony_ci * upperlimit service curve.  the link-sharing scheduler does not schedule
4962306a36Sopenharmony_ci * a class whose fit-time exceeds the current time.
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include <linux/kernel.h>
5362306a36Sopenharmony_ci#include <linux/module.h>
5462306a36Sopenharmony_ci#include <linux/types.h>
5562306a36Sopenharmony_ci#include <linux/errno.h>
5662306a36Sopenharmony_ci#include <linux/compiler.h>
5762306a36Sopenharmony_ci#include <linux/spinlock.h>
5862306a36Sopenharmony_ci#include <linux/skbuff.h>
5962306a36Sopenharmony_ci#include <linux/string.h>
6062306a36Sopenharmony_ci#include <linux/slab.h>
6162306a36Sopenharmony_ci#include <linux/list.h>
6262306a36Sopenharmony_ci#include <linux/rbtree.h>
6362306a36Sopenharmony_ci#include <linux/init.h>
6462306a36Sopenharmony_ci#include <linux/rtnetlink.h>
6562306a36Sopenharmony_ci#include <linux/pkt_sched.h>
6662306a36Sopenharmony_ci#include <net/netlink.h>
6762306a36Sopenharmony_ci#include <net/pkt_sched.h>
6862306a36Sopenharmony_ci#include <net/pkt_cls.h>
6962306a36Sopenharmony_ci#include <asm/div64.h>
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * kernel internal service curve representation:
7362306a36Sopenharmony_ci *   coordinates are given by 64 bit unsigned integers.
7462306a36Sopenharmony_ci *   x-axis: unit is clock count.
7562306a36Sopenharmony_ci *   y-axis: unit is byte.
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci *   The service curve parameters are converted to the internal
7862306a36Sopenharmony_ci *   representation. The slope values are scaled to avoid overflow.
7962306a36Sopenharmony_ci *   the inverse slope values as well as the y-projection of the 1st
8062306a36Sopenharmony_ci *   segment are kept in order to avoid 64-bit divide operations
8162306a36Sopenharmony_ci *   that are expensive on 32-bit architectures.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistruct internal_sc {
8562306a36Sopenharmony_ci	u64	sm1;	/* scaled slope of the 1st segment */
8662306a36Sopenharmony_ci	u64	ism1;	/* scaled inverse-slope of the 1st segment */
8762306a36Sopenharmony_ci	u64	dx;	/* the x-projection of the 1st segment */
8862306a36Sopenharmony_ci	u64	dy;	/* the y-projection of the 1st segment */
8962306a36Sopenharmony_ci	u64	sm2;	/* scaled slope of the 2nd segment */
9062306a36Sopenharmony_ci	u64	ism2;	/* scaled inverse-slope of the 2nd segment */
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* runtime service curve */
9462306a36Sopenharmony_cistruct runtime_sc {
9562306a36Sopenharmony_ci	u64	x;	/* current starting position on x-axis */
9662306a36Sopenharmony_ci	u64	y;	/* current starting position on y-axis */
9762306a36Sopenharmony_ci	u64	sm1;	/* scaled slope of the 1st segment */
9862306a36Sopenharmony_ci	u64	ism1;	/* scaled inverse-slope of the 1st segment */
9962306a36Sopenharmony_ci	u64	dx;	/* the x-projection of the 1st segment */
10062306a36Sopenharmony_ci	u64	dy;	/* the y-projection of the 1st segment */
10162306a36Sopenharmony_ci	u64	sm2;	/* scaled slope of the 2nd segment */
10262306a36Sopenharmony_ci	u64	ism2;	/* scaled inverse-slope of the 2nd segment */
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cienum hfsc_class_flags {
10662306a36Sopenharmony_ci	HFSC_RSC = 0x1,
10762306a36Sopenharmony_ci	HFSC_FSC = 0x2,
10862306a36Sopenharmony_ci	HFSC_USC = 0x4
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistruct hfsc_class {
11262306a36Sopenharmony_ci	struct Qdisc_class_common cl_common;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	struct gnet_stats_basic_sync bstats;
11562306a36Sopenharmony_ci	struct gnet_stats_queue qstats;
11662306a36Sopenharmony_ci	struct net_rate_estimator __rcu *rate_est;
11762306a36Sopenharmony_ci	struct tcf_proto __rcu *filter_list; /* filter list */
11862306a36Sopenharmony_ci	struct tcf_block *block;
11962306a36Sopenharmony_ci	unsigned int	level;		/* class level in hierarchy */
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	struct hfsc_sched *sched;	/* scheduler data */
12262306a36Sopenharmony_ci	struct hfsc_class *cl_parent;	/* parent class */
12362306a36Sopenharmony_ci	struct list_head siblings;	/* sibling classes */
12462306a36Sopenharmony_ci	struct list_head children;	/* child classes */
12562306a36Sopenharmony_ci	struct Qdisc	*qdisc;		/* leaf qdisc */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	struct rb_node el_node;		/* qdisc's eligible tree member */
12862306a36Sopenharmony_ci	struct rb_root vt_tree;		/* active children sorted by cl_vt */
12962306a36Sopenharmony_ci	struct rb_node vt_node;		/* parent's vt_tree member */
13062306a36Sopenharmony_ci	struct rb_root cf_tree;		/* active children sorted by cl_f */
13162306a36Sopenharmony_ci	struct rb_node cf_node;		/* parent's cf_heap member */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	u64	cl_total;		/* total work in bytes */
13462306a36Sopenharmony_ci	u64	cl_cumul;		/* cumulative work in bytes done by
13562306a36Sopenharmony_ci					   real-time criteria */
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	u64	cl_d;			/* deadline*/
13862306a36Sopenharmony_ci	u64	cl_e;			/* eligible time */
13962306a36Sopenharmony_ci	u64	cl_vt;			/* virtual time */
14062306a36Sopenharmony_ci	u64	cl_f;			/* time when this class will fit for
14162306a36Sopenharmony_ci					   link-sharing, max(myf, cfmin) */
14262306a36Sopenharmony_ci	u64	cl_myf;			/* my fit-time (calculated from this
14362306a36Sopenharmony_ci					   class's own upperlimit curve) */
14462306a36Sopenharmony_ci	u64	cl_cfmin;		/* earliest children's fit-time (used
14562306a36Sopenharmony_ci					   with cl_myf to obtain cl_f) */
14662306a36Sopenharmony_ci	u64	cl_cvtmin;		/* minimal virtual time among the
14762306a36Sopenharmony_ci					   children fit for link-sharing
14862306a36Sopenharmony_ci					   (monotonic within a period) */
14962306a36Sopenharmony_ci	u64	cl_vtadj;		/* intra-period cumulative vt
15062306a36Sopenharmony_ci					   adjustment */
15162306a36Sopenharmony_ci	u64	cl_cvtoff;		/* largest virtual time seen among
15262306a36Sopenharmony_ci					   the children */
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	struct internal_sc cl_rsc;	/* internal real-time service curve */
15562306a36Sopenharmony_ci	struct internal_sc cl_fsc;	/* internal fair service curve */
15662306a36Sopenharmony_ci	struct internal_sc cl_usc;	/* internal upperlimit service curve */
15762306a36Sopenharmony_ci	struct runtime_sc cl_deadline;	/* deadline curve */
15862306a36Sopenharmony_ci	struct runtime_sc cl_eligible;	/* eligible curve */
15962306a36Sopenharmony_ci	struct runtime_sc cl_virtual;	/* virtual curve */
16062306a36Sopenharmony_ci	struct runtime_sc cl_ulimit;	/* upperlimit curve */
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	u8		cl_flags;	/* which curves are valid */
16362306a36Sopenharmony_ci	u32		cl_vtperiod;	/* vt period sequence number */
16462306a36Sopenharmony_ci	u32		cl_parentperiod;/* parent's vt period sequence number*/
16562306a36Sopenharmony_ci	u32		cl_nactive;	/* number of active children */
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistruct hfsc_sched {
16962306a36Sopenharmony_ci	u16	defcls;				/* default class id */
17062306a36Sopenharmony_ci	struct hfsc_class root;			/* root class */
17162306a36Sopenharmony_ci	struct Qdisc_class_hash clhash;		/* class hash */
17262306a36Sopenharmony_ci	struct rb_root eligible;		/* eligible tree */
17362306a36Sopenharmony_ci	struct qdisc_watchdog watchdog;		/* watchdog timer */
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci#define	HT_INFINITY	0xffffffffffffffffULL	/* infinite time value */
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/*
18062306a36Sopenharmony_ci * eligible tree holds backlogged classes being sorted by their eligible times.
18162306a36Sopenharmony_ci * there is one eligible tree per hfsc instance.
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void
18562306a36Sopenharmony_cieltree_insert(struct hfsc_class *cl)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct rb_node **p = &cl->sched->eligible.rb_node;
18862306a36Sopenharmony_ci	struct rb_node *parent = NULL;
18962306a36Sopenharmony_ci	struct hfsc_class *cl1;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	while (*p != NULL) {
19262306a36Sopenharmony_ci		parent = *p;
19362306a36Sopenharmony_ci		cl1 = rb_entry(parent, struct hfsc_class, el_node);
19462306a36Sopenharmony_ci		if (cl->cl_e >= cl1->cl_e)
19562306a36Sopenharmony_ci			p = &parent->rb_right;
19662306a36Sopenharmony_ci		else
19762306a36Sopenharmony_ci			p = &parent->rb_left;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	rb_link_node(&cl->el_node, parent, p);
20062306a36Sopenharmony_ci	rb_insert_color(&cl->el_node, &cl->sched->eligible);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic inline void
20462306a36Sopenharmony_cieltree_remove(struct hfsc_class *cl)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	rb_erase(&cl->el_node, &cl->sched->eligible);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic inline void
21062306a36Sopenharmony_cieltree_update(struct hfsc_class *cl)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	eltree_remove(cl);
21362306a36Sopenharmony_ci	eltree_insert(cl);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/* find the class with the minimum deadline among the eligible classes */
21762306a36Sopenharmony_cistatic inline struct hfsc_class *
21862306a36Sopenharmony_cieltree_get_mindl(struct hfsc_sched *q, u64 cur_time)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct hfsc_class *p, *cl = NULL;
22162306a36Sopenharmony_ci	struct rb_node *n;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	for (n = rb_first(&q->eligible); n != NULL; n = rb_next(n)) {
22462306a36Sopenharmony_ci		p = rb_entry(n, struct hfsc_class, el_node);
22562306a36Sopenharmony_ci		if (p->cl_e > cur_time)
22662306a36Sopenharmony_ci			break;
22762306a36Sopenharmony_ci		if (cl == NULL || p->cl_d < cl->cl_d)
22862306a36Sopenharmony_ci			cl = p;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	return cl;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/* find the class with minimum eligible time among the eligible classes */
23462306a36Sopenharmony_cistatic inline struct hfsc_class *
23562306a36Sopenharmony_cieltree_get_minel(struct hfsc_sched *q)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct rb_node *n;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	n = rb_first(&q->eligible);
24062306a36Sopenharmony_ci	if (n == NULL)
24162306a36Sopenharmony_ci		return NULL;
24262306a36Sopenharmony_ci	return rb_entry(n, struct hfsc_class, el_node);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/*
24662306a36Sopenharmony_ci * vttree holds holds backlogged child classes being sorted by their virtual
24762306a36Sopenharmony_ci * time. each intermediate class has one vttree.
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_cistatic void
25062306a36Sopenharmony_civttree_insert(struct hfsc_class *cl)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct rb_node **p = &cl->cl_parent->vt_tree.rb_node;
25362306a36Sopenharmony_ci	struct rb_node *parent = NULL;
25462306a36Sopenharmony_ci	struct hfsc_class *cl1;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	while (*p != NULL) {
25762306a36Sopenharmony_ci		parent = *p;
25862306a36Sopenharmony_ci		cl1 = rb_entry(parent, struct hfsc_class, vt_node);
25962306a36Sopenharmony_ci		if (cl->cl_vt >= cl1->cl_vt)
26062306a36Sopenharmony_ci			p = &parent->rb_right;
26162306a36Sopenharmony_ci		else
26262306a36Sopenharmony_ci			p = &parent->rb_left;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci	rb_link_node(&cl->vt_node, parent, p);
26562306a36Sopenharmony_ci	rb_insert_color(&cl->vt_node, &cl->cl_parent->vt_tree);
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic inline void
26962306a36Sopenharmony_civttree_remove(struct hfsc_class *cl)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	rb_erase(&cl->vt_node, &cl->cl_parent->vt_tree);
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic inline void
27562306a36Sopenharmony_civttree_update(struct hfsc_class *cl)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	vttree_remove(cl);
27862306a36Sopenharmony_ci	vttree_insert(cl);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic inline struct hfsc_class *
28262306a36Sopenharmony_civttree_firstfit(struct hfsc_class *cl, u64 cur_time)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct hfsc_class *p;
28562306a36Sopenharmony_ci	struct rb_node *n;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	for (n = rb_first(&cl->vt_tree); n != NULL; n = rb_next(n)) {
28862306a36Sopenharmony_ci		p = rb_entry(n, struct hfsc_class, vt_node);
28962306a36Sopenharmony_ci		if (p->cl_f <= cur_time)
29062306a36Sopenharmony_ci			return p;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci	return NULL;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/*
29662306a36Sopenharmony_ci * get the leaf class with the minimum vt in the hierarchy
29762306a36Sopenharmony_ci */
29862306a36Sopenharmony_cistatic struct hfsc_class *
29962306a36Sopenharmony_civttree_get_minvt(struct hfsc_class *cl, u64 cur_time)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	/* if root-class's cfmin is bigger than cur_time nothing to do */
30262306a36Sopenharmony_ci	if (cl->cl_cfmin > cur_time)
30362306a36Sopenharmony_ci		return NULL;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	while (cl->level > 0) {
30662306a36Sopenharmony_ci		cl = vttree_firstfit(cl, cur_time);
30762306a36Sopenharmony_ci		if (cl == NULL)
30862306a36Sopenharmony_ci			return NULL;
30962306a36Sopenharmony_ci		/*
31062306a36Sopenharmony_ci		 * update parent's cl_cvtmin.
31162306a36Sopenharmony_ci		 */
31262306a36Sopenharmony_ci		if (cl->cl_parent->cl_cvtmin < cl->cl_vt)
31362306a36Sopenharmony_ci			cl->cl_parent->cl_cvtmin = cl->cl_vt;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci	return cl;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void
31962306a36Sopenharmony_cicftree_insert(struct hfsc_class *cl)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct rb_node **p = &cl->cl_parent->cf_tree.rb_node;
32262306a36Sopenharmony_ci	struct rb_node *parent = NULL;
32362306a36Sopenharmony_ci	struct hfsc_class *cl1;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	while (*p != NULL) {
32662306a36Sopenharmony_ci		parent = *p;
32762306a36Sopenharmony_ci		cl1 = rb_entry(parent, struct hfsc_class, cf_node);
32862306a36Sopenharmony_ci		if (cl->cl_f >= cl1->cl_f)
32962306a36Sopenharmony_ci			p = &parent->rb_right;
33062306a36Sopenharmony_ci		else
33162306a36Sopenharmony_ci			p = &parent->rb_left;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci	rb_link_node(&cl->cf_node, parent, p);
33462306a36Sopenharmony_ci	rb_insert_color(&cl->cf_node, &cl->cl_parent->cf_tree);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic inline void
33862306a36Sopenharmony_cicftree_remove(struct hfsc_class *cl)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	rb_erase(&cl->cf_node, &cl->cl_parent->cf_tree);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic inline void
34462306a36Sopenharmony_cicftree_update(struct hfsc_class *cl)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	cftree_remove(cl);
34762306a36Sopenharmony_ci	cftree_insert(cl);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/*
35162306a36Sopenharmony_ci * service curve support functions
35262306a36Sopenharmony_ci *
35362306a36Sopenharmony_ci *  external service curve parameters
35462306a36Sopenharmony_ci *	m: bps
35562306a36Sopenharmony_ci *	d: us
35662306a36Sopenharmony_ci *  internal service curve parameters
35762306a36Sopenharmony_ci *	sm: (bytes/psched_us) << SM_SHIFT
35862306a36Sopenharmony_ci *	ism: (psched_us/byte) << ISM_SHIFT
35962306a36Sopenharmony_ci *	dx: psched_us
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * The clock source resolution with ktime and PSCHED_SHIFT 10 is 1.024us.
36262306a36Sopenharmony_ci *
36362306a36Sopenharmony_ci * sm and ism are scaled in order to keep effective digits.
36462306a36Sopenharmony_ci * SM_SHIFT and ISM_SHIFT are selected to keep at least 4 effective
36562306a36Sopenharmony_ci * digits in decimal using the following table.
36662306a36Sopenharmony_ci *
36762306a36Sopenharmony_ci *  bits/sec      100Kbps     1Mbps     10Mbps     100Mbps    1Gbps
36862306a36Sopenharmony_ci *  ------------+-------------------------------------------------------
36962306a36Sopenharmony_ci *  bytes/1.024us 12.8e-3    128e-3     1280e-3    12800e-3   128000e-3
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci *  1.024us/byte  78.125     7.8125     0.78125    0.078125   0.0078125
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * So, for PSCHED_SHIFT 10 we need: SM_SHIFT 20, ISM_SHIFT 18.
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_ci#define	SM_SHIFT	(30 - PSCHED_SHIFT)
37662306a36Sopenharmony_ci#define	ISM_SHIFT	(8 + PSCHED_SHIFT)
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci#define	SM_MASK		((1ULL << SM_SHIFT) - 1)
37962306a36Sopenharmony_ci#define	ISM_MASK	((1ULL << ISM_SHIFT) - 1)
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic inline u64
38262306a36Sopenharmony_ciseg_x2y(u64 x, u64 sm)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	u64 y;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/*
38762306a36Sopenharmony_ci	 * compute
38862306a36Sopenharmony_ci	 *	y = x * sm >> SM_SHIFT
38962306a36Sopenharmony_ci	 * but divide it for the upper and lower bits to avoid overflow
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	y = (x >> SM_SHIFT) * sm + (((x & SM_MASK) * sm) >> SM_SHIFT);
39262306a36Sopenharmony_ci	return y;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic inline u64
39662306a36Sopenharmony_ciseg_y2x(u64 y, u64 ism)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	u64 x;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (y == 0)
40162306a36Sopenharmony_ci		x = 0;
40262306a36Sopenharmony_ci	else if (ism == HT_INFINITY)
40362306a36Sopenharmony_ci		x = HT_INFINITY;
40462306a36Sopenharmony_ci	else {
40562306a36Sopenharmony_ci		x = (y >> ISM_SHIFT) * ism
40662306a36Sopenharmony_ci		    + (((y & ISM_MASK) * ism) >> ISM_SHIFT);
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	return x;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/* Convert m (bps) into sm (bytes/psched us) */
41262306a36Sopenharmony_cistatic u64
41362306a36Sopenharmony_cim2sm(u32 m)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	u64 sm;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	sm = ((u64)m << SM_SHIFT);
41862306a36Sopenharmony_ci	sm += PSCHED_TICKS_PER_SEC - 1;
41962306a36Sopenharmony_ci	do_div(sm, PSCHED_TICKS_PER_SEC);
42062306a36Sopenharmony_ci	return sm;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/* convert m (bps) into ism (psched us/byte) */
42462306a36Sopenharmony_cistatic u64
42562306a36Sopenharmony_cim2ism(u32 m)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	u64 ism;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (m == 0)
43062306a36Sopenharmony_ci		ism = HT_INFINITY;
43162306a36Sopenharmony_ci	else {
43262306a36Sopenharmony_ci		ism = ((u64)PSCHED_TICKS_PER_SEC << ISM_SHIFT);
43362306a36Sopenharmony_ci		ism += m - 1;
43462306a36Sopenharmony_ci		do_div(ism, m);
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci	return ism;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/* convert d (us) into dx (psched us) */
44062306a36Sopenharmony_cistatic u64
44162306a36Sopenharmony_cid2dx(u32 d)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	u64 dx;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	dx = ((u64)d * PSCHED_TICKS_PER_SEC);
44662306a36Sopenharmony_ci	dx += USEC_PER_SEC - 1;
44762306a36Sopenharmony_ci	do_div(dx, USEC_PER_SEC);
44862306a36Sopenharmony_ci	return dx;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci/* convert sm (bytes/psched us) into m (bps) */
45262306a36Sopenharmony_cistatic u32
45362306a36Sopenharmony_cism2m(u64 sm)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	u64 m;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	m = (sm * PSCHED_TICKS_PER_SEC) >> SM_SHIFT;
45862306a36Sopenharmony_ci	return (u32)m;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/* convert dx (psched us) into d (us) */
46262306a36Sopenharmony_cistatic u32
46362306a36Sopenharmony_cidx2d(u64 dx)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	u64 d;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	d = dx * USEC_PER_SEC;
46862306a36Sopenharmony_ci	do_div(d, PSCHED_TICKS_PER_SEC);
46962306a36Sopenharmony_ci	return (u32)d;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void
47362306a36Sopenharmony_cisc2isc(struct tc_service_curve *sc, struct internal_sc *isc)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	isc->sm1  = m2sm(sc->m1);
47662306a36Sopenharmony_ci	isc->ism1 = m2ism(sc->m1);
47762306a36Sopenharmony_ci	isc->dx   = d2dx(sc->d);
47862306a36Sopenharmony_ci	isc->dy   = seg_x2y(isc->dx, isc->sm1);
47962306a36Sopenharmony_ci	isc->sm2  = m2sm(sc->m2);
48062306a36Sopenharmony_ci	isc->ism2 = m2ism(sc->m2);
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci/*
48462306a36Sopenharmony_ci * initialize the runtime service curve with the given internal
48562306a36Sopenharmony_ci * service curve starting at (x, y).
48662306a36Sopenharmony_ci */
48762306a36Sopenharmony_cistatic void
48862306a36Sopenharmony_cirtsc_init(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	rtsc->x	   = x;
49162306a36Sopenharmony_ci	rtsc->y    = y;
49262306a36Sopenharmony_ci	rtsc->sm1  = isc->sm1;
49362306a36Sopenharmony_ci	rtsc->ism1 = isc->ism1;
49462306a36Sopenharmony_ci	rtsc->dx   = isc->dx;
49562306a36Sopenharmony_ci	rtsc->dy   = isc->dy;
49662306a36Sopenharmony_ci	rtsc->sm2  = isc->sm2;
49762306a36Sopenharmony_ci	rtsc->ism2 = isc->ism2;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/*
50162306a36Sopenharmony_ci * calculate the y-projection of the runtime service curve by the
50262306a36Sopenharmony_ci * given x-projection value
50362306a36Sopenharmony_ci */
50462306a36Sopenharmony_cistatic u64
50562306a36Sopenharmony_cirtsc_y2x(struct runtime_sc *rtsc, u64 y)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	u64 x;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (y < rtsc->y)
51062306a36Sopenharmony_ci		x = rtsc->x;
51162306a36Sopenharmony_ci	else if (y <= rtsc->y + rtsc->dy) {
51262306a36Sopenharmony_ci		/* x belongs to the 1st segment */
51362306a36Sopenharmony_ci		if (rtsc->dy == 0)
51462306a36Sopenharmony_ci			x = rtsc->x + rtsc->dx;
51562306a36Sopenharmony_ci		else
51662306a36Sopenharmony_ci			x = rtsc->x + seg_y2x(y - rtsc->y, rtsc->ism1);
51762306a36Sopenharmony_ci	} else {
51862306a36Sopenharmony_ci		/* x belongs to the 2nd segment */
51962306a36Sopenharmony_ci		x = rtsc->x + rtsc->dx
52062306a36Sopenharmony_ci		    + seg_y2x(y - rtsc->y - rtsc->dy, rtsc->ism2);
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci	return x;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic u64
52662306a36Sopenharmony_cirtsc_x2y(struct runtime_sc *rtsc, u64 x)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	u64 y;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (x <= rtsc->x)
53162306a36Sopenharmony_ci		y = rtsc->y;
53262306a36Sopenharmony_ci	else if (x <= rtsc->x + rtsc->dx)
53362306a36Sopenharmony_ci		/* y belongs to the 1st segment */
53462306a36Sopenharmony_ci		y = rtsc->y + seg_x2y(x - rtsc->x, rtsc->sm1);
53562306a36Sopenharmony_ci	else
53662306a36Sopenharmony_ci		/* y belongs to the 2nd segment */
53762306a36Sopenharmony_ci		y = rtsc->y + rtsc->dy
53862306a36Sopenharmony_ci		    + seg_x2y(x - rtsc->x - rtsc->dx, rtsc->sm2);
53962306a36Sopenharmony_ci	return y;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci/*
54362306a36Sopenharmony_ci * update the runtime service curve by taking the minimum of the current
54462306a36Sopenharmony_ci * runtime service curve and the service curve starting at (x, y).
54562306a36Sopenharmony_ci */
54662306a36Sopenharmony_cistatic void
54762306a36Sopenharmony_cirtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	u64 y1, y2, dx, dy;
55062306a36Sopenharmony_ci	u32 dsm;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (isc->sm1 <= isc->sm2) {
55362306a36Sopenharmony_ci		/* service curve is convex */
55462306a36Sopenharmony_ci		y1 = rtsc_x2y(rtsc, x);
55562306a36Sopenharmony_ci		if (y1 < y)
55662306a36Sopenharmony_ci			/* the current rtsc is smaller */
55762306a36Sopenharmony_ci			return;
55862306a36Sopenharmony_ci		rtsc->x = x;
55962306a36Sopenharmony_ci		rtsc->y = y;
56062306a36Sopenharmony_ci		return;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	/*
56462306a36Sopenharmony_ci	 * service curve is concave
56562306a36Sopenharmony_ci	 * compute the two y values of the current rtsc
56662306a36Sopenharmony_ci	 *	y1: at x
56762306a36Sopenharmony_ci	 *	y2: at (x + dx)
56862306a36Sopenharmony_ci	 */
56962306a36Sopenharmony_ci	y1 = rtsc_x2y(rtsc, x);
57062306a36Sopenharmony_ci	if (y1 <= y) {
57162306a36Sopenharmony_ci		/* rtsc is below isc, no change to rtsc */
57262306a36Sopenharmony_ci		return;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	y2 = rtsc_x2y(rtsc, x + isc->dx);
57662306a36Sopenharmony_ci	if (y2 >= y + isc->dy) {
57762306a36Sopenharmony_ci		/* rtsc is above isc, replace rtsc by isc */
57862306a36Sopenharmony_ci		rtsc->x = x;
57962306a36Sopenharmony_ci		rtsc->y = y;
58062306a36Sopenharmony_ci		rtsc->dx = isc->dx;
58162306a36Sopenharmony_ci		rtsc->dy = isc->dy;
58262306a36Sopenharmony_ci		return;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/*
58662306a36Sopenharmony_ci	 * the two curves intersect
58762306a36Sopenharmony_ci	 * compute the offsets (dx, dy) using the reverse
58862306a36Sopenharmony_ci	 * function of seg_x2y()
58962306a36Sopenharmony_ci	 *	seg_x2y(dx, sm1) == seg_x2y(dx, sm2) + (y1 - y)
59062306a36Sopenharmony_ci	 */
59162306a36Sopenharmony_ci	dx = (y1 - y) << SM_SHIFT;
59262306a36Sopenharmony_ci	dsm = isc->sm1 - isc->sm2;
59362306a36Sopenharmony_ci	do_div(dx, dsm);
59462306a36Sopenharmony_ci	/*
59562306a36Sopenharmony_ci	 * check if (x, y1) belongs to the 1st segment of rtsc.
59662306a36Sopenharmony_ci	 * if so, add the offset.
59762306a36Sopenharmony_ci	 */
59862306a36Sopenharmony_ci	if (rtsc->x + rtsc->dx > x)
59962306a36Sopenharmony_ci		dx += rtsc->x + rtsc->dx - x;
60062306a36Sopenharmony_ci	dy = seg_x2y(dx, isc->sm1);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	rtsc->x = x;
60362306a36Sopenharmony_ci	rtsc->y = y;
60462306a36Sopenharmony_ci	rtsc->dx = dx;
60562306a36Sopenharmony_ci	rtsc->dy = dy;
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic void
60962306a36Sopenharmony_ciinit_ed(struct hfsc_class *cl, unsigned int next_len)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	u64 cur_time = psched_get_time();
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* update the deadline curve */
61462306a36Sopenharmony_ci	rtsc_min(&cl->cl_deadline, &cl->cl_rsc, cur_time, cl->cl_cumul);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/*
61762306a36Sopenharmony_ci	 * update the eligible curve.
61862306a36Sopenharmony_ci	 * for concave, it is equal to the deadline curve.
61962306a36Sopenharmony_ci	 * for convex, it is a linear curve with slope m2.
62062306a36Sopenharmony_ci	 */
62162306a36Sopenharmony_ci	cl->cl_eligible = cl->cl_deadline;
62262306a36Sopenharmony_ci	if (cl->cl_rsc.sm1 <= cl->cl_rsc.sm2) {
62362306a36Sopenharmony_ci		cl->cl_eligible.dx = 0;
62462306a36Sopenharmony_ci		cl->cl_eligible.dy = 0;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* compute e and d */
62862306a36Sopenharmony_ci	cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
62962306a36Sopenharmony_ci	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	eltree_insert(cl);
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic void
63562306a36Sopenharmony_ciupdate_ed(struct hfsc_class *cl, unsigned int next_len)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul);
63862306a36Sopenharmony_ci	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	eltree_update(cl);
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic inline void
64462306a36Sopenharmony_ciupdate_d(struct hfsc_class *cl, unsigned int next_len)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len);
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic inline void
65062306a36Sopenharmony_ciupdate_cfmin(struct hfsc_class *cl)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct rb_node *n = rb_first(&cl->cf_tree);
65362306a36Sopenharmony_ci	struct hfsc_class *p;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (n == NULL) {
65662306a36Sopenharmony_ci		cl->cl_cfmin = 0;
65762306a36Sopenharmony_ci		return;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci	p = rb_entry(n, struct hfsc_class, cf_node);
66062306a36Sopenharmony_ci	cl->cl_cfmin = p->cl_f;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic void
66462306a36Sopenharmony_ciinit_vf(struct hfsc_class *cl, unsigned int len)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct hfsc_class *max_cl;
66762306a36Sopenharmony_ci	struct rb_node *n;
66862306a36Sopenharmony_ci	u64 vt, f, cur_time;
66962306a36Sopenharmony_ci	int go_active;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	cur_time = 0;
67262306a36Sopenharmony_ci	go_active = 1;
67362306a36Sopenharmony_ci	for (; cl->cl_parent != NULL; cl = cl->cl_parent) {
67462306a36Sopenharmony_ci		if (go_active && cl->cl_nactive++ == 0)
67562306a36Sopenharmony_ci			go_active = 1;
67662306a36Sopenharmony_ci		else
67762306a36Sopenharmony_ci			go_active = 0;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		if (go_active) {
68062306a36Sopenharmony_ci			n = rb_last(&cl->cl_parent->vt_tree);
68162306a36Sopenharmony_ci			if (n != NULL) {
68262306a36Sopenharmony_ci				max_cl = rb_entry(n, struct hfsc_class, vt_node);
68362306a36Sopenharmony_ci				/*
68462306a36Sopenharmony_ci				 * set vt to the average of the min and max
68562306a36Sopenharmony_ci				 * classes.  if the parent's period didn't
68662306a36Sopenharmony_ci				 * change, don't decrease vt of the class.
68762306a36Sopenharmony_ci				 */
68862306a36Sopenharmony_ci				vt = max_cl->cl_vt;
68962306a36Sopenharmony_ci				if (cl->cl_parent->cl_cvtmin != 0)
69062306a36Sopenharmony_ci					vt = (cl->cl_parent->cl_cvtmin + vt)/2;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci				if (cl->cl_parent->cl_vtperiod !=
69362306a36Sopenharmony_ci				    cl->cl_parentperiod || vt > cl->cl_vt)
69462306a36Sopenharmony_ci					cl->cl_vt = vt;
69562306a36Sopenharmony_ci			} else {
69662306a36Sopenharmony_ci				/*
69762306a36Sopenharmony_ci				 * first child for a new parent backlog period.
69862306a36Sopenharmony_ci				 * initialize cl_vt to the highest value seen
69962306a36Sopenharmony_ci				 * among the siblings. this is analogous to
70062306a36Sopenharmony_ci				 * what cur_time would provide in realtime case.
70162306a36Sopenharmony_ci				 */
70262306a36Sopenharmony_ci				cl->cl_vt = cl->cl_parent->cl_cvtoff;
70362306a36Sopenharmony_ci				cl->cl_parent->cl_cvtmin = 0;
70462306a36Sopenharmony_ci			}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci			/* update the virtual curve */
70762306a36Sopenharmony_ci			rtsc_min(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total);
70862306a36Sopenharmony_ci			cl->cl_vtadj = 0;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci			cl->cl_vtperiod++;  /* increment vt period */
71162306a36Sopenharmony_ci			cl->cl_parentperiod = cl->cl_parent->cl_vtperiod;
71262306a36Sopenharmony_ci			if (cl->cl_parent->cl_nactive == 0)
71362306a36Sopenharmony_ci				cl->cl_parentperiod++;
71462306a36Sopenharmony_ci			cl->cl_f = 0;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci			vttree_insert(cl);
71762306a36Sopenharmony_ci			cftree_insert(cl);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci			if (cl->cl_flags & HFSC_USC) {
72062306a36Sopenharmony_ci				/* class has upper limit curve */
72162306a36Sopenharmony_ci				if (cur_time == 0)
72262306a36Sopenharmony_ci					cur_time = psched_get_time();
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci				/* update the ulimit curve */
72562306a36Sopenharmony_ci				rtsc_min(&cl->cl_ulimit, &cl->cl_usc, cur_time,
72662306a36Sopenharmony_ci					 cl->cl_total);
72762306a36Sopenharmony_ci				/* compute myf */
72862306a36Sopenharmony_ci				cl->cl_myf = rtsc_y2x(&cl->cl_ulimit,
72962306a36Sopenharmony_ci						      cl->cl_total);
73062306a36Sopenharmony_ci			}
73162306a36Sopenharmony_ci		}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		f = max(cl->cl_myf, cl->cl_cfmin);
73462306a36Sopenharmony_ci		if (f != cl->cl_f) {
73562306a36Sopenharmony_ci			cl->cl_f = f;
73662306a36Sopenharmony_ci			cftree_update(cl);
73762306a36Sopenharmony_ci		}
73862306a36Sopenharmony_ci		update_cfmin(cl->cl_parent);
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic void
74362306a36Sopenharmony_ciupdate_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	u64 f; /* , myf_bound, delta; */
74662306a36Sopenharmony_ci	int go_passive = 0;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC)
74962306a36Sopenharmony_ci		go_passive = 1;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	for (; cl->cl_parent != NULL; cl = cl->cl_parent) {
75262306a36Sopenharmony_ci		cl->cl_total += len;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		if (!(cl->cl_flags & HFSC_FSC) || cl->cl_nactive == 0)
75562306a36Sopenharmony_ci			continue;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		if (go_passive && --cl->cl_nactive == 0)
75862306a36Sopenharmony_ci			go_passive = 1;
75962306a36Sopenharmony_ci		else
76062306a36Sopenharmony_ci			go_passive = 0;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci		/* update vt */
76362306a36Sopenharmony_ci		cl->cl_vt = rtsc_y2x(&cl->cl_virtual, cl->cl_total) + cl->cl_vtadj;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		/*
76662306a36Sopenharmony_ci		 * if vt of the class is smaller than cvtmin,
76762306a36Sopenharmony_ci		 * the class was skipped in the past due to non-fit.
76862306a36Sopenharmony_ci		 * if so, we need to adjust vtadj.
76962306a36Sopenharmony_ci		 */
77062306a36Sopenharmony_ci		if (cl->cl_vt < cl->cl_parent->cl_cvtmin) {
77162306a36Sopenharmony_ci			cl->cl_vtadj += cl->cl_parent->cl_cvtmin - cl->cl_vt;
77262306a36Sopenharmony_ci			cl->cl_vt = cl->cl_parent->cl_cvtmin;
77362306a36Sopenharmony_ci		}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		if (go_passive) {
77662306a36Sopenharmony_ci			/* no more active child, going passive */
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci			/* update cvtoff of the parent class */
77962306a36Sopenharmony_ci			if (cl->cl_vt > cl->cl_parent->cl_cvtoff)
78062306a36Sopenharmony_ci				cl->cl_parent->cl_cvtoff = cl->cl_vt;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci			/* remove this class from the vt tree */
78362306a36Sopenharmony_ci			vttree_remove(cl);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci			cftree_remove(cl);
78662306a36Sopenharmony_ci			update_cfmin(cl->cl_parent);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci			continue;
78962306a36Sopenharmony_ci		}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		/* update the vt tree */
79262306a36Sopenharmony_ci		vttree_update(cl);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		/* update f */
79562306a36Sopenharmony_ci		if (cl->cl_flags & HFSC_USC) {
79662306a36Sopenharmony_ci			cl->cl_myf = rtsc_y2x(&cl->cl_ulimit, cl->cl_total);
79762306a36Sopenharmony_ci#if 0
79862306a36Sopenharmony_ci			cl->cl_myf = cl->cl_myfadj + rtsc_y2x(&cl->cl_ulimit,
79962306a36Sopenharmony_ci							      cl->cl_total);
80062306a36Sopenharmony_ci			/*
80162306a36Sopenharmony_ci			 * This code causes classes to stay way under their
80262306a36Sopenharmony_ci			 * limit when multiple classes are used at gigabit
80362306a36Sopenharmony_ci			 * speed. needs investigation. -kaber
80462306a36Sopenharmony_ci			 */
80562306a36Sopenharmony_ci			/*
80662306a36Sopenharmony_ci			 * if myf lags behind by more than one clock tick
80762306a36Sopenharmony_ci			 * from the current time, adjust myfadj to prevent
80862306a36Sopenharmony_ci			 * a rate-limited class from going greedy.
80962306a36Sopenharmony_ci			 * in a steady state under rate-limiting, myf
81062306a36Sopenharmony_ci			 * fluctuates within one clock tick.
81162306a36Sopenharmony_ci			 */
81262306a36Sopenharmony_ci			myf_bound = cur_time - PSCHED_JIFFIE2US(1);
81362306a36Sopenharmony_ci			if (cl->cl_myf < myf_bound) {
81462306a36Sopenharmony_ci				delta = cur_time - cl->cl_myf;
81562306a36Sopenharmony_ci				cl->cl_myfadj += delta;
81662306a36Sopenharmony_ci				cl->cl_myf += delta;
81762306a36Sopenharmony_ci			}
81862306a36Sopenharmony_ci#endif
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci		f = max(cl->cl_myf, cl->cl_cfmin);
82262306a36Sopenharmony_ci		if (f != cl->cl_f) {
82362306a36Sopenharmony_ci			cl->cl_f = f;
82462306a36Sopenharmony_ci			cftree_update(cl);
82562306a36Sopenharmony_ci			update_cfmin(cl->cl_parent);
82662306a36Sopenharmony_ci		}
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cistatic unsigned int
83162306a36Sopenharmony_ciqdisc_peek_len(struct Qdisc *sch)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct sk_buff *skb;
83462306a36Sopenharmony_ci	unsigned int len;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	skb = sch->ops->peek(sch);
83762306a36Sopenharmony_ci	if (unlikely(skb == NULL)) {
83862306a36Sopenharmony_ci		qdisc_warn_nonwc("qdisc_peek_len", sch);
83962306a36Sopenharmony_ci		return 0;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci	len = qdisc_pkt_len(skb);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	return len;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void
84762306a36Sopenharmony_cihfsc_adjust_levels(struct hfsc_class *cl)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct hfsc_class *p;
85062306a36Sopenharmony_ci	unsigned int level;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	do {
85362306a36Sopenharmony_ci		level = 0;
85462306a36Sopenharmony_ci		list_for_each_entry(p, &cl->children, siblings) {
85562306a36Sopenharmony_ci			if (p->level >= level)
85662306a36Sopenharmony_ci				level = p->level + 1;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci		cl->level = level;
85962306a36Sopenharmony_ci	} while ((cl = cl->cl_parent) != NULL);
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic inline struct hfsc_class *
86362306a36Sopenharmony_cihfsc_find_class(u32 classid, struct Qdisc *sch)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
86662306a36Sopenharmony_ci	struct Qdisc_class_common *clc;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	clc = qdisc_class_find(&q->clhash, classid);
86962306a36Sopenharmony_ci	if (clc == NULL)
87062306a36Sopenharmony_ci		return NULL;
87162306a36Sopenharmony_ci	return container_of(clc, struct hfsc_class, cl_common);
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic void
87562306a36Sopenharmony_cihfsc_change_rsc(struct hfsc_class *cl, struct tc_service_curve *rsc,
87662306a36Sopenharmony_ci		u64 cur_time)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	sc2isc(rsc, &cl->cl_rsc);
87962306a36Sopenharmony_ci	rtsc_init(&cl->cl_deadline, &cl->cl_rsc, cur_time, cl->cl_cumul);
88062306a36Sopenharmony_ci	cl->cl_eligible = cl->cl_deadline;
88162306a36Sopenharmony_ci	if (cl->cl_rsc.sm1 <= cl->cl_rsc.sm2) {
88262306a36Sopenharmony_ci		cl->cl_eligible.dx = 0;
88362306a36Sopenharmony_ci		cl->cl_eligible.dy = 0;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci	cl->cl_flags |= HFSC_RSC;
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic void
88962306a36Sopenharmony_cihfsc_change_fsc(struct hfsc_class *cl, struct tc_service_curve *fsc)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	sc2isc(fsc, &cl->cl_fsc);
89262306a36Sopenharmony_ci	rtsc_init(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total);
89362306a36Sopenharmony_ci	cl->cl_flags |= HFSC_FSC;
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic void
89762306a36Sopenharmony_cihfsc_change_usc(struct hfsc_class *cl, struct tc_service_curve *usc,
89862306a36Sopenharmony_ci		u64 cur_time)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	sc2isc(usc, &cl->cl_usc);
90162306a36Sopenharmony_ci	rtsc_init(&cl->cl_ulimit, &cl->cl_usc, cur_time, cl->cl_total);
90262306a36Sopenharmony_ci	cl->cl_flags |= HFSC_USC;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic void
90662306a36Sopenharmony_cihfsc_upgrade_rt(struct hfsc_class *cl)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	cl->cl_fsc = cl->cl_rsc;
90962306a36Sopenharmony_ci	rtsc_init(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total);
91062306a36Sopenharmony_ci	cl->cl_flags |= HFSC_FSC;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic const struct nla_policy hfsc_policy[TCA_HFSC_MAX + 1] = {
91462306a36Sopenharmony_ci	[TCA_HFSC_RSC]	= { .len = sizeof(struct tc_service_curve) },
91562306a36Sopenharmony_ci	[TCA_HFSC_FSC]	= { .len = sizeof(struct tc_service_curve) },
91662306a36Sopenharmony_ci	[TCA_HFSC_USC]	= { .len = sizeof(struct tc_service_curve) },
91762306a36Sopenharmony_ci};
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic int
92062306a36Sopenharmony_cihfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
92162306a36Sopenharmony_ci		  struct nlattr **tca, unsigned long *arg,
92262306a36Sopenharmony_ci		  struct netlink_ext_ack *extack)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
92562306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)*arg;
92662306a36Sopenharmony_ci	struct hfsc_class *parent = NULL;
92762306a36Sopenharmony_ci	struct nlattr *opt = tca[TCA_OPTIONS];
92862306a36Sopenharmony_ci	struct nlattr *tb[TCA_HFSC_MAX + 1];
92962306a36Sopenharmony_ci	struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL;
93062306a36Sopenharmony_ci	u64 cur_time;
93162306a36Sopenharmony_ci	int err;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (opt == NULL)
93462306a36Sopenharmony_ci		return -EINVAL;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_HFSC_MAX, opt, hfsc_policy,
93762306a36Sopenharmony_ci					  NULL);
93862306a36Sopenharmony_ci	if (err < 0)
93962306a36Sopenharmony_ci		return err;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (tb[TCA_HFSC_RSC]) {
94262306a36Sopenharmony_ci		rsc = nla_data(tb[TCA_HFSC_RSC]);
94362306a36Sopenharmony_ci		if (rsc->m1 == 0 && rsc->m2 == 0)
94462306a36Sopenharmony_ci			rsc = NULL;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (tb[TCA_HFSC_FSC]) {
94862306a36Sopenharmony_ci		fsc = nla_data(tb[TCA_HFSC_FSC]);
94962306a36Sopenharmony_ci		if (fsc->m1 == 0 && fsc->m2 == 0)
95062306a36Sopenharmony_ci			fsc = NULL;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (tb[TCA_HFSC_USC]) {
95462306a36Sopenharmony_ci		usc = nla_data(tb[TCA_HFSC_USC]);
95562306a36Sopenharmony_ci		if (usc->m1 == 0 && usc->m2 == 0)
95662306a36Sopenharmony_ci			usc = NULL;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	if (cl != NULL) {
96062306a36Sopenharmony_ci		int old_flags;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci		if (parentid) {
96362306a36Sopenharmony_ci			if (cl->cl_parent &&
96462306a36Sopenharmony_ci			    cl->cl_parent->cl_common.classid != parentid)
96562306a36Sopenharmony_ci				return -EINVAL;
96662306a36Sopenharmony_ci			if (cl->cl_parent == NULL && parentid != TC_H_ROOT)
96762306a36Sopenharmony_ci				return -EINVAL;
96862306a36Sopenharmony_ci		}
96962306a36Sopenharmony_ci		cur_time = psched_get_time();
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci		if (tca[TCA_RATE]) {
97262306a36Sopenharmony_ci			err = gen_replace_estimator(&cl->bstats, NULL,
97362306a36Sopenharmony_ci						    &cl->rate_est,
97462306a36Sopenharmony_ci						    NULL,
97562306a36Sopenharmony_ci						    true,
97662306a36Sopenharmony_ci						    tca[TCA_RATE]);
97762306a36Sopenharmony_ci			if (err)
97862306a36Sopenharmony_ci				return err;
97962306a36Sopenharmony_ci		}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci		sch_tree_lock(sch);
98262306a36Sopenharmony_ci		old_flags = cl->cl_flags;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		if (rsc != NULL)
98562306a36Sopenharmony_ci			hfsc_change_rsc(cl, rsc, cur_time);
98662306a36Sopenharmony_ci		if (fsc != NULL)
98762306a36Sopenharmony_ci			hfsc_change_fsc(cl, fsc);
98862306a36Sopenharmony_ci		if (usc != NULL)
98962306a36Sopenharmony_ci			hfsc_change_usc(cl, usc, cur_time);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		if (cl->qdisc->q.qlen != 0) {
99262306a36Sopenharmony_ci			int len = qdisc_peek_len(cl->qdisc);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci			if (cl->cl_flags & HFSC_RSC) {
99562306a36Sopenharmony_ci				if (old_flags & HFSC_RSC)
99662306a36Sopenharmony_ci					update_ed(cl, len);
99762306a36Sopenharmony_ci				else
99862306a36Sopenharmony_ci					init_ed(cl, len);
99962306a36Sopenharmony_ci			}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci			if (cl->cl_flags & HFSC_FSC) {
100262306a36Sopenharmony_ci				if (old_flags & HFSC_FSC)
100362306a36Sopenharmony_ci					update_vf(cl, 0, cur_time);
100462306a36Sopenharmony_ci				else
100562306a36Sopenharmony_ci					init_vf(cl, len);
100662306a36Sopenharmony_ci			}
100762306a36Sopenharmony_ci		}
100862306a36Sopenharmony_ci		sch_tree_unlock(sch);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		return 0;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	if (parentid == TC_H_ROOT)
101462306a36Sopenharmony_ci		return -EEXIST;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	parent = &q->root;
101762306a36Sopenharmony_ci	if (parentid) {
101862306a36Sopenharmony_ci		parent = hfsc_find_class(parentid, sch);
101962306a36Sopenharmony_ci		if (parent == NULL)
102062306a36Sopenharmony_ci			return -ENOENT;
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (classid == 0 || TC_H_MAJ(classid ^ sch->handle) != 0)
102462306a36Sopenharmony_ci		return -EINVAL;
102562306a36Sopenharmony_ci	if (hfsc_find_class(classid, sch))
102662306a36Sopenharmony_ci		return -EEXIST;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (rsc == NULL && fsc == NULL)
102962306a36Sopenharmony_ci		return -EINVAL;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	cl = kzalloc(sizeof(struct hfsc_class), GFP_KERNEL);
103262306a36Sopenharmony_ci	if (cl == NULL)
103362306a36Sopenharmony_ci		return -ENOBUFS;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	err = tcf_block_get(&cl->block, &cl->filter_list, sch, extack);
103662306a36Sopenharmony_ci	if (err) {
103762306a36Sopenharmony_ci		kfree(cl);
103862306a36Sopenharmony_ci		return err;
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (tca[TCA_RATE]) {
104262306a36Sopenharmony_ci		err = gen_new_estimator(&cl->bstats, NULL, &cl->rate_est,
104362306a36Sopenharmony_ci					NULL, true, tca[TCA_RATE]);
104462306a36Sopenharmony_ci		if (err) {
104562306a36Sopenharmony_ci			tcf_block_put(cl->block);
104662306a36Sopenharmony_ci			kfree(cl);
104762306a36Sopenharmony_ci			return err;
104862306a36Sopenharmony_ci		}
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	if (rsc != NULL)
105262306a36Sopenharmony_ci		hfsc_change_rsc(cl, rsc, 0);
105362306a36Sopenharmony_ci	if (fsc != NULL)
105462306a36Sopenharmony_ci		hfsc_change_fsc(cl, fsc);
105562306a36Sopenharmony_ci	if (usc != NULL)
105662306a36Sopenharmony_ci		hfsc_change_usc(cl, usc, 0);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	cl->cl_common.classid = classid;
105962306a36Sopenharmony_ci	cl->sched     = q;
106062306a36Sopenharmony_ci	cl->cl_parent = parent;
106162306a36Sopenharmony_ci	cl->qdisc = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
106262306a36Sopenharmony_ci				      classid, NULL);
106362306a36Sopenharmony_ci	if (cl->qdisc == NULL)
106462306a36Sopenharmony_ci		cl->qdisc = &noop_qdisc;
106562306a36Sopenharmony_ci	else
106662306a36Sopenharmony_ci		qdisc_hash_add(cl->qdisc, true);
106762306a36Sopenharmony_ci	INIT_LIST_HEAD(&cl->children);
106862306a36Sopenharmony_ci	cl->vt_tree = RB_ROOT;
106962306a36Sopenharmony_ci	cl->cf_tree = RB_ROOT;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	sch_tree_lock(sch);
107262306a36Sopenharmony_ci	/* Check if the inner class is a misconfigured 'rt' */
107362306a36Sopenharmony_ci	if (!(parent->cl_flags & HFSC_FSC) && parent != &q->root) {
107462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
107562306a36Sopenharmony_ci			       "Forced curve change on parent 'rt' to 'sc'");
107662306a36Sopenharmony_ci		hfsc_upgrade_rt(parent);
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci	qdisc_class_hash_insert(&q->clhash, &cl->cl_common);
107962306a36Sopenharmony_ci	list_add_tail(&cl->siblings, &parent->children);
108062306a36Sopenharmony_ci	if (parent->level == 0)
108162306a36Sopenharmony_ci		qdisc_purge_queue(parent->qdisc);
108262306a36Sopenharmony_ci	hfsc_adjust_levels(parent);
108362306a36Sopenharmony_ci	sch_tree_unlock(sch);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	qdisc_class_hash_grow(sch, &q->clhash);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	*arg = (unsigned long)cl;
108862306a36Sopenharmony_ci	return 0;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic void
109262306a36Sopenharmony_cihfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
109362306a36Sopenharmony_ci{
109462306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	tcf_block_put(cl->block);
109762306a36Sopenharmony_ci	qdisc_put(cl->qdisc);
109862306a36Sopenharmony_ci	gen_kill_estimator(&cl->rate_est);
109962306a36Sopenharmony_ci	if (cl != &q->root)
110062306a36Sopenharmony_ci		kfree(cl);
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistatic int
110462306a36Sopenharmony_cihfsc_delete_class(struct Qdisc *sch, unsigned long arg,
110562306a36Sopenharmony_ci		  struct netlink_ext_ack *extack)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
110862306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)arg;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	if (cl->level > 0 || qdisc_class_in_use(&cl->cl_common) ||
111162306a36Sopenharmony_ci	    cl == &q->root) {
111262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "HFSC class in use");
111362306a36Sopenharmony_ci		return -EBUSY;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	sch_tree_lock(sch);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	list_del(&cl->siblings);
111962306a36Sopenharmony_ci	hfsc_adjust_levels(cl->cl_parent);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	qdisc_purge_queue(cl->qdisc);
112262306a36Sopenharmony_ci	qdisc_class_hash_remove(&q->clhash, &cl->cl_common);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	sch_tree_unlock(sch);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	hfsc_destroy_class(sch, cl);
112762306a36Sopenharmony_ci	return 0;
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic struct hfsc_class *
113162306a36Sopenharmony_cihfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
113262306a36Sopenharmony_ci{
113362306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
113462306a36Sopenharmony_ci	struct hfsc_class *head, *cl;
113562306a36Sopenharmony_ci	struct tcf_result res;
113662306a36Sopenharmony_ci	struct tcf_proto *tcf;
113762306a36Sopenharmony_ci	int result;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	if (TC_H_MAJ(skb->priority ^ sch->handle) == 0 &&
114062306a36Sopenharmony_ci	    (cl = hfsc_find_class(skb->priority, sch)) != NULL)
114162306a36Sopenharmony_ci		if (cl->level == 0)
114262306a36Sopenharmony_ci			return cl;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
114562306a36Sopenharmony_ci	head = &q->root;
114662306a36Sopenharmony_ci	tcf = rcu_dereference_bh(q->root.filter_list);
114762306a36Sopenharmony_ci	while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {
114862306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
114962306a36Sopenharmony_ci		switch (result) {
115062306a36Sopenharmony_ci		case TC_ACT_QUEUED:
115162306a36Sopenharmony_ci		case TC_ACT_STOLEN:
115262306a36Sopenharmony_ci		case TC_ACT_TRAP:
115362306a36Sopenharmony_ci			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
115462306a36Sopenharmony_ci			fallthrough;
115562306a36Sopenharmony_ci		case TC_ACT_SHOT:
115662306a36Sopenharmony_ci			return NULL;
115762306a36Sopenharmony_ci		}
115862306a36Sopenharmony_ci#endif
115962306a36Sopenharmony_ci		cl = (struct hfsc_class *)res.class;
116062306a36Sopenharmony_ci		if (!cl) {
116162306a36Sopenharmony_ci			cl = hfsc_find_class(res.classid, sch);
116262306a36Sopenharmony_ci			if (!cl)
116362306a36Sopenharmony_ci				break; /* filter selected invalid classid */
116462306a36Sopenharmony_ci			if (cl->level >= head->level)
116562306a36Sopenharmony_ci				break; /* filter may only point downwards */
116662306a36Sopenharmony_ci		}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		if (cl->level == 0)
116962306a36Sopenharmony_ci			return cl; /* hit leaf class */
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		/* apply inner filter chain */
117262306a36Sopenharmony_ci		tcf = rcu_dereference_bh(cl->filter_list);
117362306a36Sopenharmony_ci		head = cl;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	/* classification failed, try default class */
117762306a36Sopenharmony_ci	cl = hfsc_find_class(TC_H_MAKE(TC_H_MAJ(sch->handle), q->defcls), sch);
117862306a36Sopenharmony_ci	if (cl == NULL || cl->level > 0)
117962306a36Sopenharmony_ci		return NULL;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	return cl;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic int
118562306a36Sopenharmony_cihfsc_graft_class(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
118662306a36Sopenharmony_ci		 struct Qdisc **old, struct netlink_ext_ack *extack)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)arg;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (cl->level > 0)
119162306a36Sopenharmony_ci		return -EINVAL;
119262306a36Sopenharmony_ci	if (new == NULL) {
119362306a36Sopenharmony_ci		new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
119462306a36Sopenharmony_ci					cl->cl_common.classid, NULL);
119562306a36Sopenharmony_ci		if (new == NULL)
119662306a36Sopenharmony_ci			new = &noop_qdisc;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	*old = qdisc_replace(sch, new, &cl->qdisc);
120062306a36Sopenharmony_ci	return 0;
120162306a36Sopenharmony_ci}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_cistatic struct Qdisc *
120462306a36Sopenharmony_cihfsc_class_leaf(struct Qdisc *sch, unsigned long arg)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)arg;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (cl->level == 0)
120962306a36Sopenharmony_ci		return cl->qdisc;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	return NULL;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic void
121562306a36Sopenharmony_cihfsc_qlen_notify(struct Qdisc *sch, unsigned long arg)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)arg;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	/* vttree is now handled in update_vf() so that update_vf(cl, 0, 0)
122062306a36Sopenharmony_ci	 * needs to be called explicitly to remove a class from vttree.
122162306a36Sopenharmony_ci	 */
122262306a36Sopenharmony_ci	update_vf(cl, 0, 0);
122362306a36Sopenharmony_ci	if (cl->cl_flags & HFSC_RSC)
122462306a36Sopenharmony_ci		eltree_remove(cl);
122562306a36Sopenharmony_ci}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_cistatic unsigned long
122862306a36Sopenharmony_cihfsc_search_class(struct Qdisc *sch, u32 classid)
122962306a36Sopenharmony_ci{
123062306a36Sopenharmony_ci	return (unsigned long)hfsc_find_class(classid, sch);
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic unsigned long
123462306a36Sopenharmony_cihfsc_bind_tcf(struct Qdisc *sch, unsigned long parent, u32 classid)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct hfsc_class *p = (struct hfsc_class *)parent;
123762306a36Sopenharmony_ci	struct hfsc_class *cl = hfsc_find_class(classid, sch);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	if (cl != NULL) {
124062306a36Sopenharmony_ci		if (p != NULL && p->level <= cl->level)
124162306a36Sopenharmony_ci			return 0;
124262306a36Sopenharmony_ci		qdisc_class_get(&cl->cl_common);
124362306a36Sopenharmony_ci	}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	return (unsigned long)cl;
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic void
124962306a36Sopenharmony_cihfsc_unbind_tcf(struct Qdisc *sch, unsigned long arg)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)arg;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	qdisc_class_put(&cl->cl_common);
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_cistatic struct tcf_block *hfsc_tcf_block(struct Qdisc *sch, unsigned long arg,
125762306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
126062306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)arg;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	if (cl == NULL)
126362306a36Sopenharmony_ci		cl = &q->root;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	return cl->block;
126662306a36Sopenharmony_ci}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_cistatic int
126962306a36Sopenharmony_cihfsc_dump_sc(struct sk_buff *skb, int attr, struct internal_sc *sc)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	struct tc_service_curve tsc;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	tsc.m1 = sm2m(sc->sm1);
127462306a36Sopenharmony_ci	tsc.d  = dx2d(sc->dx);
127562306a36Sopenharmony_ci	tsc.m2 = sm2m(sc->sm2);
127662306a36Sopenharmony_ci	if (nla_put(skb, attr, sizeof(tsc), &tsc))
127762306a36Sopenharmony_ci		goto nla_put_failure;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	return skb->len;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci nla_put_failure:
128262306a36Sopenharmony_ci	return -1;
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_cistatic int
128662306a36Sopenharmony_cihfsc_dump_curves(struct sk_buff *skb, struct hfsc_class *cl)
128762306a36Sopenharmony_ci{
128862306a36Sopenharmony_ci	if ((cl->cl_flags & HFSC_RSC) &&
128962306a36Sopenharmony_ci	    (hfsc_dump_sc(skb, TCA_HFSC_RSC, &cl->cl_rsc) < 0))
129062306a36Sopenharmony_ci		goto nla_put_failure;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	if ((cl->cl_flags & HFSC_FSC) &&
129362306a36Sopenharmony_ci	    (hfsc_dump_sc(skb, TCA_HFSC_FSC, &cl->cl_fsc) < 0))
129462306a36Sopenharmony_ci		goto nla_put_failure;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	if ((cl->cl_flags & HFSC_USC) &&
129762306a36Sopenharmony_ci	    (hfsc_dump_sc(skb, TCA_HFSC_USC, &cl->cl_usc) < 0))
129862306a36Sopenharmony_ci		goto nla_put_failure;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	return skb->len;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci nla_put_failure:
130362306a36Sopenharmony_ci	return -1;
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_cistatic int
130762306a36Sopenharmony_cihfsc_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb,
130862306a36Sopenharmony_ci		struct tcmsg *tcm)
130962306a36Sopenharmony_ci{
131062306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)arg;
131162306a36Sopenharmony_ci	struct nlattr *nest;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	tcm->tcm_parent = cl->cl_parent ? cl->cl_parent->cl_common.classid :
131462306a36Sopenharmony_ci					  TC_H_ROOT;
131562306a36Sopenharmony_ci	tcm->tcm_handle = cl->cl_common.classid;
131662306a36Sopenharmony_ci	if (cl->level == 0)
131762306a36Sopenharmony_ci		tcm->tcm_info = cl->qdisc->handle;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
132062306a36Sopenharmony_ci	if (nest == NULL)
132162306a36Sopenharmony_ci		goto nla_put_failure;
132262306a36Sopenharmony_ci	if (hfsc_dump_curves(skb, cl) < 0)
132362306a36Sopenharmony_ci		goto nla_put_failure;
132462306a36Sopenharmony_ci	return nla_nest_end(skb, nest);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci nla_put_failure:
132762306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
132862306a36Sopenharmony_ci	return -EMSGSIZE;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic int
133262306a36Sopenharmony_cihfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg,
133362306a36Sopenharmony_ci	struct gnet_dump *d)
133462306a36Sopenharmony_ci{
133562306a36Sopenharmony_ci	struct hfsc_class *cl = (struct hfsc_class *)arg;
133662306a36Sopenharmony_ci	struct tc_hfsc_stats xstats;
133762306a36Sopenharmony_ci	__u32 qlen;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	qdisc_qstats_qlen_backlog(cl->qdisc, &qlen, &cl->qstats.backlog);
134062306a36Sopenharmony_ci	xstats.level   = cl->level;
134162306a36Sopenharmony_ci	xstats.period  = cl->cl_vtperiod;
134262306a36Sopenharmony_ci	xstats.work    = cl->cl_total;
134362306a36Sopenharmony_ci	xstats.rtwork  = cl->cl_cumul;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 ||
134662306a36Sopenharmony_ci	    gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
134762306a36Sopenharmony_ci	    gnet_stats_copy_queue(d, NULL, &cl->qstats, qlen) < 0)
134862306a36Sopenharmony_ci		return -1;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_cistatic void
135662306a36Sopenharmony_cihfsc_walk(struct Qdisc *sch, struct qdisc_walker *arg)
135762306a36Sopenharmony_ci{
135862306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
135962306a36Sopenharmony_ci	struct hfsc_class *cl;
136062306a36Sopenharmony_ci	unsigned int i;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	if (arg->stop)
136362306a36Sopenharmony_ci		return;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	for (i = 0; i < q->clhash.hashsize; i++) {
136662306a36Sopenharmony_ci		hlist_for_each_entry(cl, &q->clhash.hash[i],
136762306a36Sopenharmony_ci				     cl_common.hnode) {
136862306a36Sopenharmony_ci			if (!tc_qdisc_stats_dump(sch, (unsigned long)cl, arg))
136962306a36Sopenharmony_ci				return;
137062306a36Sopenharmony_ci		}
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_cistatic void
137562306a36Sopenharmony_cihfsc_schedule_watchdog(struct Qdisc *sch)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
137862306a36Sopenharmony_ci	struct hfsc_class *cl;
137962306a36Sopenharmony_ci	u64 next_time = 0;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	cl = eltree_get_minel(q);
138262306a36Sopenharmony_ci	if (cl)
138362306a36Sopenharmony_ci		next_time = cl->cl_e;
138462306a36Sopenharmony_ci	if (q->root.cl_cfmin != 0) {
138562306a36Sopenharmony_ci		if (next_time == 0 || next_time > q->root.cl_cfmin)
138662306a36Sopenharmony_ci			next_time = q->root.cl_cfmin;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci	if (next_time)
138962306a36Sopenharmony_ci		qdisc_watchdog_schedule(&q->watchdog, next_time);
139062306a36Sopenharmony_ci}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_cistatic int
139362306a36Sopenharmony_cihfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt,
139462306a36Sopenharmony_ci		struct netlink_ext_ack *extack)
139562306a36Sopenharmony_ci{
139662306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
139762306a36Sopenharmony_ci	struct tc_hfsc_qopt *qopt;
139862306a36Sopenharmony_ci	int err;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	qdisc_watchdog_init(&q->watchdog, sch);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	if (!opt || nla_len(opt) < sizeof(*qopt))
140362306a36Sopenharmony_ci		return -EINVAL;
140462306a36Sopenharmony_ci	qopt = nla_data(opt);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	q->defcls = qopt->defcls;
140762306a36Sopenharmony_ci	err = qdisc_class_hash_init(&q->clhash);
140862306a36Sopenharmony_ci	if (err < 0)
140962306a36Sopenharmony_ci		return err;
141062306a36Sopenharmony_ci	q->eligible = RB_ROOT;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	err = tcf_block_get(&q->root.block, &q->root.filter_list, sch, extack);
141362306a36Sopenharmony_ci	if (err)
141462306a36Sopenharmony_ci		return err;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	gnet_stats_basic_sync_init(&q->root.bstats);
141762306a36Sopenharmony_ci	q->root.cl_common.classid = sch->handle;
141862306a36Sopenharmony_ci	q->root.sched   = q;
141962306a36Sopenharmony_ci	q->root.qdisc = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
142062306a36Sopenharmony_ci					  sch->handle, NULL);
142162306a36Sopenharmony_ci	if (q->root.qdisc == NULL)
142262306a36Sopenharmony_ci		q->root.qdisc = &noop_qdisc;
142362306a36Sopenharmony_ci	else
142462306a36Sopenharmony_ci		qdisc_hash_add(q->root.qdisc, true);
142562306a36Sopenharmony_ci	INIT_LIST_HEAD(&q->root.children);
142662306a36Sopenharmony_ci	q->root.vt_tree = RB_ROOT;
142762306a36Sopenharmony_ci	q->root.cf_tree = RB_ROOT;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	qdisc_class_hash_insert(&q->clhash, &q->root.cl_common);
143062306a36Sopenharmony_ci	qdisc_class_hash_grow(sch, &q->clhash);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	return 0;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic int
143662306a36Sopenharmony_cihfsc_change_qdisc(struct Qdisc *sch, struct nlattr *opt,
143762306a36Sopenharmony_ci		  struct netlink_ext_ack *extack)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
144062306a36Sopenharmony_ci	struct tc_hfsc_qopt *qopt;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	if (nla_len(opt) < sizeof(*qopt))
144362306a36Sopenharmony_ci		return -EINVAL;
144462306a36Sopenharmony_ci	qopt = nla_data(opt);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	sch_tree_lock(sch);
144762306a36Sopenharmony_ci	q->defcls = qopt->defcls;
144862306a36Sopenharmony_ci	sch_tree_unlock(sch);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	return 0;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic void
145462306a36Sopenharmony_cihfsc_reset_class(struct hfsc_class *cl)
145562306a36Sopenharmony_ci{
145662306a36Sopenharmony_ci	cl->cl_total        = 0;
145762306a36Sopenharmony_ci	cl->cl_cumul        = 0;
145862306a36Sopenharmony_ci	cl->cl_d            = 0;
145962306a36Sopenharmony_ci	cl->cl_e            = 0;
146062306a36Sopenharmony_ci	cl->cl_vt           = 0;
146162306a36Sopenharmony_ci	cl->cl_vtadj        = 0;
146262306a36Sopenharmony_ci	cl->cl_cvtmin       = 0;
146362306a36Sopenharmony_ci	cl->cl_cvtoff       = 0;
146462306a36Sopenharmony_ci	cl->cl_vtperiod     = 0;
146562306a36Sopenharmony_ci	cl->cl_parentperiod = 0;
146662306a36Sopenharmony_ci	cl->cl_f            = 0;
146762306a36Sopenharmony_ci	cl->cl_myf          = 0;
146862306a36Sopenharmony_ci	cl->cl_cfmin        = 0;
146962306a36Sopenharmony_ci	cl->cl_nactive      = 0;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	cl->vt_tree = RB_ROOT;
147262306a36Sopenharmony_ci	cl->cf_tree = RB_ROOT;
147362306a36Sopenharmony_ci	qdisc_reset(cl->qdisc);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	if (cl->cl_flags & HFSC_RSC)
147662306a36Sopenharmony_ci		rtsc_init(&cl->cl_deadline, &cl->cl_rsc, 0, 0);
147762306a36Sopenharmony_ci	if (cl->cl_flags & HFSC_FSC)
147862306a36Sopenharmony_ci		rtsc_init(&cl->cl_virtual, &cl->cl_fsc, 0, 0);
147962306a36Sopenharmony_ci	if (cl->cl_flags & HFSC_USC)
148062306a36Sopenharmony_ci		rtsc_init(&cl->cl_ulimit, &cl->cl_usc, 0, 0);
148162306a36Sopenharmony_ci}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_cistatic void
148462306a36Sopenharmony_cihfsc_reset_qdisc(struct Qdisc *sch)
148562306a36Sopenharmony_ci{
148662306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
148762306a36Sopenharmony_ci	struct hfsc_class *cl;
148862306a36Sopenharmony_ci	unsigned int i;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	for (i = 0; i < q->clhash.hashsize; i++) {
149162306a36Sopenharmony_ci		hlist_for_each_entry(cl, &q->clhash.hash[i], cl_common.hnode)
149262306a36Sopenharmony_ci			hfsc_reset_class(cl);
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci	q->eligible = RB_ROOT;
149562306a36Sopenharmony_ci	qdisc_watchdog_cancel(&q->watchdog);
149662306a36Sopenharmony_ci}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistatic void
149962306a36Sopenharmony_cihfsc_destroy_qdisc(struct Qdisc *sch)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
150262306a36Sopenharmony_ci	struct hlist_node *next;
150362306a36Sopenharmony_ci	struct hfsc_class *cl;
150462306a36Sopenharmony_ci	unsigned int i;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	for (i = 0; i < q->clhash.hashsize; i++) {
150762306a36Sopenharmony_ci		hlist_for_each_entry(cl, &q->clhash.hash[i], cl_common.hnode) {
150862306a36Sopenharmony_ci			tcf_block_put(cl->block);
150962306a36Sopenharmony_ci			cl->block = NULL;
151062306a36Sopenharmony_ci		}
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci	for (i = 0; i < q->clhash.hashsize; i++) {
151362306a36Sopenharmony_ci		hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i],
151462306a36Sopenharmony_ci					  cl_common.hnode)
151562306a36Sopenharmony_ci			hfsc_destroy_class(sch, cl);
151662306a36Sopenharmony_ci	}
151762306a36Sopenharmony_ci	qdisc_class_hash_destroy(&q->clhash);
151862306a36Sopenharmony_ci	qdisc_watchdog_cancel(&q->watchdog);
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_cistatic int
152262306a36Sopenharmony_cihfsc_dump_qdisc(struct Qdisc *sch, struct sk_buff *skb)
152362306a36Sopenharmony_ci{
152462306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
152562306a36Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
152662306a36Sopenharmony_ci	struct tc_hfsc_qopt qopt;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	qopt.defcls = q->defcls;
152962306a36Sopenharmony_ci	if (nla_put(skb, TCA_OPTIONS, sizeof(qopt), &qopt))
153062306a36Sopenharmony_ci		goto nla_put_failure;
153162306a36Sopenharmony_ci	return skb->len;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci nla_put_failure:
153462306a36Sopenharmony_ci	nlmsg_trim(skb, b);
153562306a36Sopenharmony_ci	return -1;
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_cistatic int
153962306a36Sopenharmony_cihfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
154062306a36Sopenharmony_ci{
154162306a36Sopenharmony_ci	unsigned int len = qdisc_pkt_len(skb);
154262306a36Sopenharmony_ci	struct hfsc_class *cl;
154362306a36Sopenharmony_ci	int err;
154462306a36Sopenharmony_ci	bool first;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	cl = hfsc_classify(skb, sch, &err);
154762306a36Sopenharmony_ci	if (cl == NULL) {
154862306a36Sopenharmony_ci		if (err & __NET_XMIT_BYPASS)
154962306a36Sopenharmony_ci			qdisc_qstats_drop(sch);
155062306a36Sopenharmony_ci		__qdisc_drop(skb, to_free);
155162306a36Sopenharmony_ci		return err;
155262306a36Sopenharmony_ci	}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	first = !cl->qdisc->q.qlen;
155562306a36Sopenharmony_ci	err = qdisc_enqueue(skb, cl->qdisc, to_free);
155662306a36Sopenharmony_ci	if (unlikely(err != NET_XMIT_SUCCESS)) {
155762306a36Sopenharmony_ci		if (net_xmit_drop_count(err)) {
155862306a36Sopenharmony_ci			cl->qstats.drops++;
155962306a36Sopenharmony_ci			qdisc_qstats_drop(sch);
156062306a36Sopenharmony_ci		}
156162306a36Sopenharmony_ci		return err;
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	if (first) {
156562306a36Sopenharmony_ci		if (cl->cl_flags & HFSC_RSC)
156662306a36Sopenharmony_ci			init_ed(cl, len);
156762306a36Sopenharmony_ci		if (cl->cl_flags & HFSC_FSC)
156862306a36Sopenharmony_ci			init_vf(cl, len);
156962306a36Sopenharmony_ci		/*
157062306a36Sopenharmony_ci		 * If this is the first packet, isolate the head so an eventual
157162306a36Sopenharmony_ci		 * head drop before the first dequeue operation has no chance
157262306a36Sopenharmony_ci		 * to invalidate the deadline.
157362306a36Sopenharmony_ci		 */
157462306a36Sopenharmony_ci		if (cl->cl_flags & HFSC_RSC)
157562306a36Sopenharmony_ci			cl->qdisc->ops->peek(cl->qdisc);
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	sch->qstats.backlog += len;
158062306a36Sopenharmony_ci	sch->q.qlen++;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	return NET_XMIT_SUCCESS;
158362306a36Sopenharmony_ci}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_cistatic struct sk_buff *
158662306a36Sopenharmony_cihfsc_dequeue(struct Qdisc *sch)
158762306a36Sopenharmony_ci{
158862306a36Sopenharmony_ci	struct hfsc_sched *q = qdisc_priv(sch);
158962306a36Sopenharmony_ci	struct hfsc_class *cl;
159062306a36Sopenharmony_ci	struct sk_buff *skb;
159162306a36Sopenharmony_ci	u64 cur_time;
159262306a36Sopenharmony_ci	unsigned int next_len;
159362306a36Sopenharmony_ci	int realtime = 0;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	if (sch->q.qlen == 0)
159662306a36Sopenharmony_ci		return NULL;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	cur_time = psched_get_time();
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	/*
160162306a36Sopenharmony_ci	 * if there are eligible classes, use real-time criteria.
160262306a36Sopenharmony_ci	 * find the class with the minimum deadline among
160362306a36Sopenharmony_ci	 * the eligible classes.
160462306a36Sopenharmony_ci	 */
160562306a36Sopenharmony_ci	cl = eltree_get_mindl(q, cur_time);
160662306a36Sopenharmony_ci	if (cl) {
160762306a36Sopenharmony_ci		realtime = 1;
160862306a36Sopenharmony_ci	} else {
160962306a36Sopenharmony_ci		/*
161062306a36Sopenharmony_ci		 * use link-sharing criteria
161162306a36Sopenharmony_ci		 * get the class with the minimum vt in the hierarchy
161262306a36Sopenharmony_ci		 */
161362306a36Sopenharmony_ci		cl = vttree_get_minvt(&q->root, cur_time);
161462306a36Sopenharmony_ci		if (cl == NULL) {
161562306a36Sopenharmony_ci			qdisc_qstats_overlimit(sch);
161662306a36Sopenharmony_ci			hfsc_schedule_watchdog(sch);
161762306a36Sopenharmony_ci			return NULL;
161862306a36Sopenharmony_ci		}
161962306a36Sopenharmony_ci	}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	skb = qdisc_dequeue_peeked(cl->qdisc);
162262306a36Sopenharmony_ci	if (skb == NULL) {
162362306a36Sopenharmony_ci		qdisc_warn_nonwc("HFSC", cl->qdisc);
162462306a36Sopenharmony_ci		return NULL;
162562306a36Sopenharmony_ci	}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	bstats_update(&cl->bstats, skb);
162862306a36Sopenharmony_ci	update_vf(cl, qdisc_pkt_len(skb), cur_time);
162962306a36Sopenharmony_ci	if (realtime)
163062306a36Sopenharmony_ci		cl->cl_cumul += qdisc_pkt_len(skb);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	if (cl->cl_flags & HFSC_RSC) {
163362306a36Sopenharmony_ci		if (cl->qdisc->q.qlen != 0) {
163462306a36Sopenharmony_ci			/* update ed */
163562306a36Sopenharmony_ci			next_len = qdisc_peek_len(cl->qdisc);
163662306a36Sopenharmony_ci			if (realtime)
163762306a36Sopenharmony_ci				update_ed(cl, next_len);
163862306a36Sopenharmony_ci			else
163962306a36Sopenharmony_ci				update_d(cl, next_len);
164062306a36Sopenharmony_ci		} else {
164162306a36Sopenharmony_ci			/* the class becomes passive */
164262306a36Sopenharmony_ci			eltree_remove(cl);
164362306a36Sopenharmony_ci		}
164462306a36Sopenharmony_ci	}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	qdisc_bstats_update(sch, skb);
164762306a36Sopenharmony_ci	qdisc_qstats_backlog_dec(sch, skb);
164862306a36Sopenharmony_ci	sch->q.qlen--;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	return skb;
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cistatic const struct Qdisc_class_ops hfsc_class_ops = {
165462306a36Sopenharmony_ci	.change		= hfsc_change_class,
165562306a36Sopenharmony_ci	.delete		= hfsc_delete_class,
165662306a36Sopenharmony_ci	.graft		= hfsc_graft_class,
165762306a36Sopenharmony_ci	.leaf		= hfsc_class_leaf,
165862306a36Sopenharmony_ci	.qlen_notify	= hfsc_qlen_notify,
165962306a36Sopenharmony_ci	.find		= hfsc_search_class,
166062306a36Sopenharmony_ci	.bind_tcf	= hfsc_bind_tcf,
166162306a36Sopenharmony_ci	.unbind_tcf	= hfsc_unbind_tcf,
166262306a36Sopenharmony_ci	.tcf_block	= hfsc_tcf_block,
166362306a36Sopenharmony_ci	.dump		= hfsc_dump_class,
166462306a36Sopenharmony_ci	.dump_stats	= hfsc_dump_class_stats,
166562306a36Sopenharmony_ci	.walk		= hfsc_walk
166662306a36Sopenharmony_ci};
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_cistatic struct Qdisc_ops hfsc_qdisc_ops __read_mostly = {
166962306a36Sopenharmony_ci	.id		= "hfsc",
167062306a36Sopenharmony_ci	.init		= hfsc_init_qdisc,
167162306a36Sopenharmony_ci	.change		= hfsc_change_qdisc,
167262306a36Sopenharmony_ci	.reset		= hfsc_reset_qdisc,
167362306a36Sopenharmony_ci	.destroy	= hfsc_destroy_qdisc,
167462306a36Sopenharmony_ci	.dump		= hfsc_dump_qdisc,
167562306a36Sopenharmony_ci	.enqueue	= hfsc_enqueue,
167662306a36Sopenharmony_ci	.dequeue	= hfsc_dequeue,
167762306a36Sopenharmony_ci	.peek		= qdisc_peek_dequeued,
167862306a36Sopenharmony_ci	.cl_ops		= &hfsc_class_ops,
167962306a36Sopenharmony_ci	.priv_size	= sizeof(struct hfsc_sched),
168062306a36Sopenharmony_ci	.owner		= THIS_MODULE
168162306a36Sopenharmony_ci};
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cistatic int __init
168462306a36Sopenharmony_cihfsc_init(void)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	return register_qdisc(&hfsc_qdisc_ops);
168762306a36Sopenharmony_ci}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic void __exit
169062306a36Sopenharmony_cihfsc_cleanup(void)
169162306a36Sopenharmony_ci{
169262306a36Sopenharmony_ci	unregister_qdisc(&hfsc_qdisc_ops);
169362306a36Sopenharmony_ci}
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
169662306a36Sopenharmony_cimodule_init(hfsc_init);
169762306a36Sopenharmony_cimodule_exit(hfsc_cleanup);
1698