162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/sch_gred.c Generic Random Early Detection queue. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: J Hadi Salim (hadi@cyberus.ca) 1998-2002 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * 991129: - Bug fix with grio mode 862306a36Sopenharmony_ci * - a better sing. AvgQ mode with Grio(WRED) 962306a36Sopenharmony_ci * - A finer grained VQ dequeue based on suggestion 1062306a36Sopenharmony_ci * from Ren Liu 1162306a36Sopenharmony_ci * - More error checks 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * For all the glorious comments look at include/net/red.h 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/skbuff.h> 2162306a36Sopenharmony_ci#include <net/pkt_cls.h> 2262306a36Sopenharmony_ci#include <net/pkt_sched.h> 2362306a36Sopenharmony_ci#include <net/red.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define GRED_DEF_PRIO (MAX_DPs / 2) 2662306a36Sopenharmony_ci#define GRED_VQ_MASK (MAX_DPs - 1) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define GRED_VQ_RED_FLAGS (TC_RED_ECN | TC_RED_HARDDROP) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct gred_sched_data; 3162306a36Sopenharmony_cistruct gred_sched; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct gred_sched_data { 3462306a36Sopenharmony_ci u32 limit; /* HARD maximal queue length */ 3562306a36Sopenharmony_ci u32 DP; /* the drop parameters */ 3662306a36Sopenharmony_ci u32 red_flags; /* virtualQ version of red_flags */ 3762306a36Sopenharmony_ci u64 bytesin; /* bytes seen on virtualQ so far*/ 3862306a36Sopenharmony_ci u32 packetsin; /* packets seen on virtualQ so far*/ 3962306a36Sopenharmony_ci u32 backlog; /* bytes on the virtualQ */ 4062306a36Sopenharmony_ci u8 prio; /* the prio of this vq */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci struct red_parms parms; 4362306a36Sopenharmony_ci struct red_vars vars; 4462306a36Sopenharmony_ci struct red_stats stats; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cienum { 4862306a36Sopenharmony_ci GRED_WRED_MODE = 1, 4962306a36Sopenharmony_ci GRED_RIO_MODE, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct gred_sched { 5362306a36Sopenharmony_ci struct gred_sched_data *tab[MAX_DPs]; 5462306a36Sopenharmony_ci unsigned long flags; 5562306a36Sopenharmony_ci u32 red_flags; 5662306a36Sopenharmony_ci u32 DPs; 5762306a36Sopenharmony_ci u32 def; 5862306a36Sopenharmony_ci struct red_vars wred_set; 5962306a36Sopenharmony_ci struct tc_gred_qopt_offload *opt; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic inline int gred_wred_mode(struct gred_sched *table) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci return test_bit(GRED_WRED_MODE, &table->flags); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic inline void gred_enable_wred_mode(struct gred_sched *table) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci __set_bit(GRED_WRED_MODE, &table->flags); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic inline void gred_disable_wred_mode(struct gred_sched *table) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci __clear_bit(GRED_WRED_MODE, &table->flags); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic inline int gred_rio_mode(struct gred_sched *table) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci return test_bit(GRED_RIO_MODE, &table->flags); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic inline void gred_enable_rio_mode(struct gred_sched *table) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci __set_bit(GRED_RIO_MODE, &table->flags); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline void gred_disable_rio_mode(struct gred_sched *table) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci __clear_bit(GRED_RIO_MODE, &table->flags); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic inline int gred_wred_mode_check(struct Qdisc *sch) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 9562306a36Sopenharmony_ci int i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Really ugly O(n^2) but shouldn't be necessary too frequent. */ 9862306a36Sopenharmony_ci for (i = 0; i < table->DPs; i++) { 9962306a36Sopenharmony_ci struct gred_sched_data *q = table->tab[i]; 10062306a36Sopenharmony_ci int n; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (q == NULL) 10362306a36Sopenharmony_ci continue; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci for (n = i + 1; n < table->DPs; n++) 10662306a36Sopenharmony_ci if (table->tab[n] && table->tab[n]->prio == q->prio) 10762306a36Sopenharmony_ci return 1; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline unsigned int gred_backlog(struct gred_sched *table, 11462306a36Sopenharmony_ci struct gred_sched_data *q, 11562306a36Sopenharmony_ci struct Qdisc *sch) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci if (gred_wred_mode(table)) 11862306a36Sopenharmony_ci return sch->qstats.backlog; 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci return q->backlog; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline u16 tc_index_to_dp(struct sk_buff *skb) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return skb->tc_index & GRED_VQ_MASK; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic inline void gred_load_wred_set(const struct gred_sched *table, 12962306a36Sopenharmony_ci struct gred_sched_data *q) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci q->vars.qavg = table->wred_set.qavg; 13262306a36Sopenharmony_ci q->vars.qidlestart = table->wred_set.qidlestart; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic inline void gred_store_wred_set(struct gred_sched *table, 13662306a36Sopenharmony_ci struct gred_sched_data *q) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci table->wred_set.qavg = q->vars.qavg; 13962306a36Sopenharmony_ci table->wred_set.qidlestart = q->vars.qidlestart; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int gred_use_ecn(struct gred_sched_data *q) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci return q->red_flags & TC_RED_ECN; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int gred_use_harddrop(struct gred_sched_data *q) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return q->red_flags & TC_RED_HARDDROP; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic bool gred_per_vq_red_flags_used(struct gred_sched *table) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci unsigned int i; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Local per-vq flags couldn't have been set unless global are 0 */ 15762306a36Sopenharmony_ci if (table->red_flags) 15862306a36Sopenharmony_ci return false; 15962306a36Sopenharmony_ci for (i = 0; i < MAX_DPs; i++) 16062306a36Sopenharmony_ci if (table->tab[i] && table->tab[i]->red_flags) 16162306a36Sopenharmony_ci return true; 16262306a36Sopenharmony_ci return false; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch, 16662306a36Sopenharmony_ci struct sk_buff **to_free) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct gred_sched_data *q = NULL; 16962306a36Sopenharmony_ci struct gred_sched *t = qdisc_priv(sch); 17062306a36Sopenharmony_ci unsigned long qavg = 0; 17162306a36Sopenharmony_ci u16 dp = tc_index_to_dp(skb); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (dp >= t->DPs || (q = t->tab[dp]) == NULL) { 17462306a36Sopenharmony_ci dp = t->def; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci q = t->tab[dp]; 17762306a36Sopenharmony_ci if (!q) { 17862306a36Sopenharmony_ci /* Pass through packets not assigned to a DP 17962306a36Sopenharmony_ci * if no default DP has been configured. This 18062306a36Sopenharmony_ci * allows for DP flows to be left untouched. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= 18362306a36Sopenharmony_ci sch->limit)) 18462306a36Sopenharmony_ci return qdisc_enqueue_tail(skb, sch); 18562306a36Sopenharmony_ci else 18662306a36Sopenharmony_ci goto drop; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* fix tc_index? --could be controversial but needed for 19062306a36Sopenharmony_ci requeueing */ 19162306a36Sopenharmony_ci skb->tc_index = (skb->tc_index & ~GRED_VQ_MASK) | dp; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* sum up all the qaves of prios < ours to get the new qave */ 19562306a36Sopenharmony_ci if (!gred_wred_mode(t) && gred_rio_mode(t)) { 19662306a36Sopenharmony_ci int i; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (i = 0; i < t->DPs; i++) { 19962306a36Sopenharmony_ci if (t->tab[i] && t->tab[i]->prio < q->prio && 20062306a36Sopenharmony_ci !red_is_idling(&t->tab[i]->vars)) 20162306a36Sopenharmony_ci qavg += t->tab[i]->vars.qavg; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci q->packetsin++; 20762306a36Sopenharmony_ci q->bytesin += qdisc_pkt_len(skb); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (gred_wred_mode(t)) 21062306a36Sopenharmony_ci gred_load_wred_set(t, q); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci q->vars.qavg = red_calc_qavg(&q->parms, 21362306a36Sopenharmony_ci &q->vars, 21462306a36Sopenharmony_ci gred_backlog(t, q, sch)); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (red_is_idling(&q->vars)) 21762306a36Sopenharmony_ci red_end_of_idle_period(&q->vars); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (gred_wred_mode(t)) 22062306a36Sopenharmony_ci gred_store_wred_set(t, q); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci switch (red_action(&q->parms, &q->vars, q->vars.qavg + qavg)) { 22362306a36Sopenharmony_ci case RED_DONT_MARK: 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci case RED_PROB_MARK: 22762306a36Sopenharmony_ci qdisc_qstats_overlimit(sch); 22862306a36Sopenharmony_ci if (!gred_use_ecn(q) || !INET_ECN_set_ce(skb)) { 22962306a36Sopenharmony_ci q->stats.prob_drop++; 23062306a36Sopenharmony_ci goto congestion_drop; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci q->stats.prob_mark++; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci case RED_HARD_MARK: 23762306a36Sopenharmony_ci qdisc_qstats_overlimit(sch); 23862306a36Sopenharmony_ci if (gred_use_harddrop(q) || !gred_use_ecn(q) || 23962306a36Sopenharmony_ci !INET_ECN_set_ce(skb)) { 24062306a36Sopenharmony_ci q->stats.forced_drop++; 24162306a36Sopenharmony_ci goto congestion_drop; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci q->stats.forced_mark++; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (gred_backlog(t, q, sch) + qdisc_pkt_len(skb) <= q->limit) { 24862306a36Sopenharmony_ci q->backlog += qdisc_pkt_len(skb); 24962306a36Sopenharmony_ci return qdisc_enqueue_tail(skb, sch); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci q->stats.pdrop++; 25362306a36Sopenharmony_cidrop: 25462306a36Sopenharmony_ci return qdisc_drop(skb, sch, to_free); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cicongestion_drop: 25762306a36Sopenharmony_ci qdisc_drop(skb, sch, to_free); 25862306a36Sopenharmony_ci return NET_XMIT_CN; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic struct sk_buff *gred_dequeue(struct Qdisc *sch) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct sk_buff *skb; 26462306a36Sopenharmony_ci struct gred_sched *t = qdisc_priv(sch); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci skb = qdisc_dequeue_head(sch); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (skb) { 26962306a36Sopenharmony_ci struct gred_sched_data *q; 27062306a36Sopenharmony_ci u16 dp = tc_index_to_dp(skb); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (dp >= t->DPs || (q = t->tab[dp]) == NULL) { 27362306a36Sopenharmony_ci net_warn_ratelimited("GRED: Unable to relocate VQ 0x%x after dequeue, screwing up backlog\n", 27462306a36Sopenharmony_ci tc_index_to_dp(skb)); 27562306a36Sopenharmony_ci } else { 27662306a36Sopenharmony_ci q->backlog -= qdisc_pkt_len(skb); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (gred_wred_mode(t)) { 27962306a36Sopenharmony_ci if (!sch->qstats.backlog) 28062306a36Sopenharmony_ci red_start_of_idle_period(&t->wred_set); 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci if (!q->backlog) 28362306a36Sopenharmony_ci red_start_of_idle_period(&q->vars); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return skb; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return NULL; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void gred_reset(struct Qdisc *sch) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int i; 29662306a36Sopenharmony_ci struct gred_sched *t = qdisc_priv(sch); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci qdisc_reset_queue(sch); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (i = 0; i < t->DPs; i++) { 30162306a36Sopenharmony_ci struct gred_sched_data *q = t->tab[i]; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!q) 30462306a36Sopenharmony_ci continue; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci red_restart(&q->vars); 30762306a36Sopenharmony_ci q->backlog = 0; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic void gred_offload(struct Qdisc *sch, enum tc_gred_command command) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 31462306a36Sopenharmony_ci struct net_device *dev = qdisc_dev(sch); 31562306a36Sopenharmony_ci struct tc_gred_qopt_offload *opt = table->opt; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) 31862306a36Sopenharmony_ci return; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci memset(opt, 0, sizeof(*opt)); 32162306a36Sopenharmony_ci opt->command = command; 32262306a36Sopenharmony_ci opt->handle = sch->handle; 32362306a36Sopenharmony_ci opt->parent = sch->parent; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (command == TC_GRED_REPLACE) { 32662306a36Sopenharmony_ci unsigned int i; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci opt->set.grio_on = gred_rio_mode(table); 32962306a36Sopenharmony_ci opt->set.wred_on = gred_wred_mode(table); 33062306a36Sopenharmony_ci opt->set.dp_cnt = table->DPs; 33162306a36Sopenharmony_ci opt->set.dp_def = table->def; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci for (i = 0; i < table->DPs; i++) { 33462306a36Sopenharmony_ci struct gred_sched_data *q = table->tab[i]; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!q) 33762306a36Sopenharmony_ci continue; 33862306a36Sopenharmony_ci opt->set.tab[i].present = true; 33962306a36Sopenharmony_ci opt->set.tab[i].limit = q->limit; 34062306a36Sopenharmony_ci opt->set.tab[i].prio = q->prio; 34162306a36Sopenharmony_ci opt->set.tab[i].min = q->parms.qth_min >> q->parms.Wlog; 34262306a36Sopenharmony_ci opt->set.tab[i].max = q->parms.qth_max >> q->parms.Wlog; 34362306a36Sopenharmony_ci opt->set.tab[i].is_ecn = gred_use_ecn(q); 34462306a36Sopenharmony_ci opt->set.tab[i].is_harddrop = gred_use_harddrop(q); 34562306a36Sopenharmony_ci opt->set.tab[i].probability = q->parms.max_P; 34662306a36Sopenharmony_ci opt->set.tab[i].backlog = &q->backlog; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci opt->set.qstats = &sch->qstats; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_GRED, opt); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int gred_offload_dump_stats(struct Qdisc *sch) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 35762306a36Sopenharmony_ci struct tc_gred_qopt_offload *hw_stats; 35862306a36Sopenharmony_ci u64 bytes = 0, packets = 0; 35962306a36Sopenharmony_ci unsigned int i; 36062306a36Sopenharmony_ci int ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci hw_stats = kzalloc(sizeof(*hw_stats), GFP_KERNEL); 36362306a36Sopenharmony_ci if (!hw_stats) 36462306a36Sopenharmony_ci return -ENOMEM; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci hw_stats->command = TC_GRED_STATS; 36762306a36Sopenharmony_ci hw_stats->handle = sch->handle; 36862306a36Sopenharmony_ci hw_stats->parent = sch->parent; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci for (i = 0; i < MAX_DPs; i++) { 37162306a36Sopenharmony_ci gnet_stats_basic_sync_init(&hw_stats->stats.bstats[i]); 37262306a36Sopenharmony_ci if (table->tab[i]) 37362306a36Sopenharmony_ci hw_stats->stats.xstats[i] = &table->tab[i]->stats; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci ret = qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_GRED, hw_stats); 37762306a36Sopenharmony_ci /* Even if driver returns failure adjust the stats - in case offload 37862306a36Sopenharmony_ci * ended but driver still wants to adjust the values. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci sch_tree_lock(sch); 38162306a36Sopenharmony_ci for (i = 0; i < MAX_DPs; i++) { 38262306a36Sopenharmony_ci if (!table->tab[i]) 38362306a36Sopenharmony_ci continue; 38462306a36Sopenharmony_ci table->tab[i]->packetsin += u64_stats_read(&hw_stats->stats.bstats[i].packets); 38562306a36Sopenharmony_ci table->tab[i]->bytesin += u64_stats_read(&hw_stats->stats.bstats[i].bytes); 38662306a36Sopenharmony_ci table->tab[i]->backlog += hw_stats->stats.qstats[i].backlog; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci bytes += u64_stats_read(&hw_stats->stats.bstats[i].bytes); 38962306a36Sopenharmony_ci packets += u64_stats_read(&hw_stats->stats.bstats[i].packets); 39062306a36Sopenharmony_ci sch->qstats.qlen += hw_stats->stats.qstats[i].qlen; 39162306a36Sopenharmony_ci sch->qstats.backlog += hw_stats->stats.qstats[i].backlog; 39262306a36Sopenharmony_ci sch->qstats.drops += hw_stats->stats.qstats[i].drops; 39362306a36Sopenharmony_ci sch->qstats.requeues += hw_stats->stats.qstats[i].requeues; 39462306a36Sopenharmony_ci sch->qstats.overlimits += hw_stats->stats.qstats[i].overlimits; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci _bstats_update(&sch->bstats, bytes, packets); 39762306a36Sopenharmony_ci sch_tree_unlock(sch); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci kfree(hw_stats); 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic inline void gred_destroy_vq(struct gred_sched_data *q) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci kfree(q); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps, 40962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 41262306a36Sopenharmony_ci struct tc_gred_sopt *sopt; 41362306a36Sopenharmony_ci bool red_flags_changed; 41462306a36Sopenharmony_ci int i; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (!dps) 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci sopt = nla_data(dps); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (sopt->DPs > MAX_DPs) { 42262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "number of virtual queues too high"); 42362306a36Sopenharmony_ci return -EINVAL; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci if (sopt->DPs == 0) { 42662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 42762306a36Sopenharmony_ci "number of virtual queues can't be 0"); 42862306a36Sopenharmony_ci return -EINVAL; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci if (sopt->def_DP >= sopt->DPs) { 43162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "default virtual queue above virtual queue count"); 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci if (sopt->flags && gred_per_vq_red_flags_used(table)) { 43562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "can't set per-Qdisc RED flags when per-virtual queue flags are used"); 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci sch_tree_lock(sch); 44062306a36Sopenharmony_ci table->DPs = sopt->DPs; 44162306a36Sopenharmony_ci table->def = sopt->def_DP; 44262306a36Sopenharmony_ci red_flags_changed = table->red_flags != sopt->flags; 44362306a36Sopenharmony_ci table->red_flags = sopt->flags; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Every entry point to GRED is synchronized with the above code 44762306a36Sopenharmony_ci * and the DP is checked against DPs, i.e. shadowed VQs can no 44862306a36Sopenharmony_ci * longer be found so we can unlock right here. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci sch_tree_unlock(sch); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (sopt->grio) { 45362306a36Sopenharmony_ci gred_enable_rio_mode(table); 45462306a36Sopenharmony_ci gred_disable_wred_mode(table); 45562306a36Sopenharmony_ci if (gred_wred_mode_check(sch)) 45662306a36Sopenharmony_ci gred_enable_wred_mode(table); 45762306a36Sopenharmony_ci } else { 45862306a36Sopenharmony_ci gred_disable_rio_mode(table); 45962306a36Sopenharmony_ci gred_disable_wred_mode(table); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (red_flags_changed) 46362306a36Sopenharmony_ci for (i = 0; i < table->DPs; i++) 46462306a36Sopenharmony_ci if (table->tab[i]) 46562306a36Sopenharmony_ci table->tab[i]->red_flags = 46662306a36Sopenharmony_ci table->red_flags & GRED_VQ_RED_FLAGS; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci for (i = table->DPs; i < MAX_DPs; i++) { 46962306a36Sopenharmony_ci if (table->tab[i]) { 47062306a36Sopenharmony_ci pr_warn("GRED: Warning: Destroying shadowed VQ 0x%x\n", 47162306a36Sopenharmony_ci i); 47262306a36Sopenharmony_ci gred_destroy_vq(table->tab[i]); 47362306a36Sopenharmony_ci table->tab[i] = NULL; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci gred_offload(sch, TC_GRED_REPLACE); 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic inline int gred_change_vq(struct Qdisc *sch, int dp, 48262306a36Sopenharmony_ci struct tc_gred_qopt *ctl, int prio, 48362306a36Sopenharmony_ci u8 *stab, u32 max_P, 48462306a36Sopenharmony_ci struct gred_sched_data **prealloc, 48562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 48862306a36Sopenharmony_ci struct gred_sched_data *q = table->tab[dp]; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Scell_log, stab)) { 49162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "invalid RED parameters"); 49262306a36Sopenharmony_ci return -EINVAL; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (!q) { 49662306a36Sopenharmony_ci table->tab[dp] = q = *prealloc; 49762306a36Sopenharmony_ci *prealloc = NULL; 49862306a36Sopenharmony_ci if (!q) 49962306a36Sopenharmony_ci return -ENOMEM; 50062306a36Sopenharmony_ci q->red_flags = table->red_flags & GRED_VQ_RED_FLAGS; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci q->DP = dp; 50462306a36Sopenharmony_ci q->prio = prio; 50562306a36Sopenharmony_ci if (ctl->limit > sch->limit) 50662306a36Sopenharmony_ci q->limit = sch->limit; 50762306a36Sopenharmony_ci else 50862306a36Sopenharmony_ci q->limit = ctl->limit; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (q->backlog == 0) 51162306a36Sopenharmony_ci red_end_of_idle_period(&q->vars); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci red_set_parms(&q->parms, 51462306a36Sopenharmony_ci ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Plog, 51562306a36Sopenharmony_ci ctl->Scell_log, stab, max_P); 51662306a36Sopenharmony_ci red_set_vars(&q->vars); 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic const struct nla_policy gred_vq_policy[TCA_GRED_VQ_MAX + 1] = { 52162306a36Sopenharmony_ci [TCA_GRED_VQ_DP] = { .type = NLA_U32 }, 52262306a36Sopenharmony_ci [TCA_GRED_VQ_FLAGS] = { .type = NLA_U32 }, 52362306a36Sopenharmony_ci}; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic const struct nla_policy gred_vqe_policy[TCA_GRED_VQ_ENTRY_MAX + 1] = { 52662306a36Sopenharmony_ci [TCA_GRED_VQ_ENTRY] = { .type = NLA_NESTED }, 52762306a36Sopenharmony_ci}; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic const struct nla_policy gred_policy[TCA_GRED_MAX + 1] = { 53062306a36Sopenharmony_ci [TCA_GRED_PARMS] = { .len = sizeof(struct tc_gred_qopt) }, 53162306a36Sopenharmony_ci [TCA_GRED_STAB] = { .len = 256 }, 53262306a36Sopenharmony_ci [TCA_GRED_DPS] = { .len = sizeof(struct tc_gred_sopt) }, 53362306a36Sopenharmony_ci [TCA_GRED_MAX_P] = { .type = NLA_U32 }, 53462306a36Sopenharmony_ci [TCA_GRED_LIMIT] = { .type = NLA_U32 }, 53562306a36Sopenharmony_ci [TCA_GRED_VQ_LIST] = { .type = NLA_NESTED }, 53662306a36Sopenharmony_ci}; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic void gred_vq_apply(struct gred_sched *table, const struct nlattr *entry) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct nlattr *tb[TCA_GRED_VQ_MAX + 1]; 54162306a36Sopenharmony_ci u32 dp; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci nla_parse_nested_deprecated(tb, TCA_GRED_VQ_MAX, entry, 54462306a36Sopenharmony_ci gred_vq_policy, NULL); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci dp = nla_get_u32(tb[TCA_GRED_VQ_DP]); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (tb[TCA_GRED_VQ_FLAGS]) 54962306a36Sopenharmony_ci table->tab[dp]->red_flags = nla_get_u32(tb[TCA_GRED_VQ_FLAGS]); 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic void gred_vqs_apply(struct gred_sched *table, struct nlattr *vqs) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci const struct nlattr *attr; 55562306a36Sopenharmony_ci int rem; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci nla_for_each_nested(attr, vqs, rem) { 55862306a36Sopenharmony_ci switch (nla_type(attr)) { 55962306a36Sopenharmony_ci case TCA_GRED_VQ_ENTRY: 56062306a36Sopenharmony_ci gred_vq_apply(table, attr); 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int gred_vq_validate(struct gred_sched *table, u32 cdp, 56762306a36Sopenharmony_ci const struct nlattr *entry, 56862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct nlattr *tb[TCA_GRED_VQ_MAX + 1]; 57162306a36Sopenharmony_ci int err; 57262306a36Sopenharmony_ci u32 dp; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_GRED_VQ_MAX, entry, 57562306a36Sopenharmony_ci gred_vq_policy, extack); 57662306a36Sopenharmony_ci if (err < 0) 57762306a36Sopenharmony_ci return err; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!tb[TCA_GRED_VQ_DP]) { 58062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Virtual queue with no index specified"); 58162306a36Sopenharmony_ci return -EINVAL; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci dp = nla_get_u32(tb[TCA_GRED_VQ_DP]); 58462306a36Sopenharmony_ci if (dp >= table->DPs) { 58562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Virtual queue with index out of bounds"); 58662306a36Sopenharmony_ci return -EINVAL; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci if (dp != cdp && !table->tab[dp]) { 58962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Virtual queue not yet instantiated"); 59062306a36Sopenharmony_ci return -EINVAL; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (tb[TCA_GRED_VQ_FLAGS]) { 59462306a36Sopenharmony_ci u32 red_flags = nla_get_u32(tb[TCA_GRED_VQ_FLAGS]); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (table->red_flags && table->red_flags != red_flags) { 59762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "can't change per-virtual queue RED flags when per-Qdisc flags are used"); 59862306a36Sopenharmony_ci return -EINVAL; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci if (red_flags & ~GRED_VQ_RED_FLAGS) { 60162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 60262306a36Sopenharmony_ci "invalid RED flags specified"); 60362306a36Sopenharmony_ci return -EINVAL; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int gred_vqs_validate(struct gred_sched *table, u32 cdp, 61162306a36Sopenharmony_ci struct nlattr *vqs, struct netlink_ext_ack *extack) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci const struct nlattr *attr; 61462306a36Sopenharmony_ci int rem, err; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci err = nla_validate_nested_deprecated(vqs, TCA_GRED_VQ_ENTRY_MAX, 61762306a36Sopenharmony_ci gred_vqe_policy, extack); 61862306a36Sopenharmony_ci if (err < 0) 61962306a36Sopenharmony_ci return err; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci nla_for_each_nested(attr, vqs, rem) { 62262306a36Sopenharmony_ci switch (nla_type(attr)) { 62362306a36Sopenharmony_ci case TCA_GRED_VQ_ENTRY: 62462306a36Sopenharmony_ci err = gred_vq_validate(table, cdp, attr, extack); 62562306a36Sopenharmony_ci if (err) 62662306a36Sopenharmony_ci return err; 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci default: 62962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "GRED_VQ_LIST can contain only entry attributes"); 63062306a36Sopenharmony_ci return -EINVAL; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (rem > 0) { 63562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Trailing data after parsing virtual queue list"); 63662306a36Sopenharmony_ci return -EINVAL; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int gred_change(struct Qdisc *sch, struct nlattr *opt, 64362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 64662306a36Sopenharmony_ci struct tc_gred_qopt *ctl; 64762306a36Sopenharmony_ci struct nlattr *tb[TCA_GRED_MAX + 1]; 64862306a36Sopenharmony_ci int err, prio = GRED_DEF_PRIO; 64962306a36Sopenharmony_ci u8 *stab; 65062306a36Sopenharmony_ci u32 max_P; 65162306a36Sopenharmony_ci struct gred_sched_data *prealloc; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_GRED_MAX, opt, gred_policy, 65462306a36Sopenharmony_ci extack); 65562306a36Sopenharmony_ci if (err < 0) 65662306a36Sopenharmony_ci return err; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (tb[TCA_GRED_PARMS] == NULL && tb[TCA_GRED_STAB] == NULL) { 65962306a36Sopenharmony_ci if (tb[TCA_GRED_LIMIT] != NULL) 66062306a36Sopenharmony_ci sch->limit = nla_get_u32(tb[TCA_GRED_LIMIT]); 66162306a36Sopenharmony_ci return gred_change_table_def(sch, tb[TCA_GRED_DPS], extack); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (tb[TCA_GRED_PARMS] == NULL || 66562306a36Sopenharmony_ci tb[TCA_GRED_STAB] == NULL || 66662306a36Sopenharmony_ci tb[TCA_GRED_LIMIT] != NULL) { 66762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "can't configure Qdisc and virtual queue at the same time"); 66862306a36Sopenharmony_ci return -EINVAL; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci max_P = tb[TCA_GRED_MAX_P] ? nla_get_u32(tb[TCA_GRED_MAX_P]) : 0; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci ctl = nla_data(tb[TCA_GRED_PARMS]); 67462306a36Sopenharmony_ci stab = nla_data(tb[TCA_GRED_STAB]); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (ctl->DP >= table->DPs) { 67762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "virtual queue index above virtual queue count"); 67862306a36Sopenharmony_ci return -EINVAL; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (tb[TCA_GRED_VQ_LIST]) { 68262306a36Sopenharmony_ci err = gred_vqs_validate(table, ctl->DP, tb[TCA_GRED_VQ_LIST], 68362306a36Sopenharmony_ci extack); 68462306a36Sopenharmony_ci if (err) 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (gred_rio_mode(table)) { 68962306a36Sopenharmony_ci if (ctl->prio == 0) { 69062306a36Sopenharmony_ci int def_prio = GRED_DEF_PRIO; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (table->tab[table->def]) 69362306a36Sopenharmony_ci def_prio = table->tab[table->def]->prio; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci printk(KERN_DEBUG "GRED: DP %u does not have a prio " 69662306a36Sopenharmony_ci "setting default to %d\n", ctl->DP, def_prio); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci prio = def_prio; 69962306a36Sopenharmony_ci } else 70062306a36Sopenharmony_ci prio = ctl->prio; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL); 70462306a36Sopenharmony_ci sch_tree_lock(sch); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci err = gred_change_vq(sch, ctl->DP, ctl, prio, stab, max_P, &prealloc, 70762306a36Sopenharmony_ci extack); 70862306a36Sopenharmony_ci if (err < 0) 70962306a36Sopenharmony_ci goto err_unlock_free; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (tb[TCA_GRED_VQ_LIST]) 71262306a36Sopenharmony_ci gred_vqs_apply(table, tb[TCA_GRED_VQ_LIST]); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (gred_rio_mode(table)) { 71562306a36Sopenharmony_ci gred_disable_wred_mode(table); 71662306a36Sopenharmony_ci if (gred_wred_mode_check(sch)) 71762306a36Sopenharmony_ci gred_enable_wred_mode(table); 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci sch_tree_unlock(sch); 72162306a36Sopenharmony_ci kfree(prealloc); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci gred_offload(sch, TC_GRED_REPLACE); 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cierr_unlock_free: 72762306a36Sopenharmony_ci sch_tree_unlock(sch); 72862306a36Sopenharmony_ci kfree(prealloc); 72962306a36Sopenharmony_ci return err; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int gred_init(struct Qdisc *sch, struct nlattr *opt, 73362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 73662306a36Sopenharmony_ci struct nlattr *tb[TCA_GRED_MAX + 1]; 73762306a36Sopenharmony_ci int err; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (!opt) 74062306a36Sopenharmony_ci return -EINVAL; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_GRED_MAX, opt, gred_policy, 74362306a36Sopenharmony_ci extack); 74462306a36Sopenharmony_ci if (err < 0) 74562306a36Sopenharmony_ci return err; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (tb[TCA_GRED_PARMS] || tb[TCA_GRED_STAB]) { 74862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 74962306a36Sopenharmony_ci "virtual queue configuration can't be specified at initialization time"); 75062306a36Sopenharmony_ci return -EINVAL; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (tb[TCA_GRED_LIMIT]) 75462306a36Sopenharmony_ci sch->limit = nla_get_u32(tb[TCA_GRED_LIMIT]); 75562306a36Sopenharmony_ci else 75662306a36Sopenharmony_ci sch->limit = qdisc_dev(sch)->tx_queue_len 75762306a36Sopenharmony_ci * psched_mtu(qdisc_dev(sch)); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (qdisc_dev(sch)->netdev_ops->ndo_setup_tc) { 76062306a36Sopenharmony_ci table->opt = kzalloc(sizeof(*table->opt), GFP_KERNEL); 76162306a36Sopenharmony_ci if (!table->opt) 76262306a36Sopenharmony_ci return -ENOMEM; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return gred_change_table_def(sch, tb[TCA_GRED_DPS], extack); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int gred_dump(struct Qdisc *sch, struct sk_buff *skb) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 77162306a36Sopenharmony_ci struct nlattr *parms, *vqs, *opts = NULL; 77262306a36Sopenharmony_ci int i; 77362306a36Sopenharmony_ci u32 max_p[MAX_DPs]; 77462306a36Sopenharmony_ci struct tc_gred_sopt sopt = { 77562306a36Sopenharmony_ci .DPs = table->DPs, 77662306a36Sopenharmony_ci .def_DP = table->def, 77762306a36Sopenharmony_ci .grio = gred_rio_mode(table), 77862306a36Sopenharmony_ci .flags = table->red_flags, 77962306a36Sopenharmony_ci }; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (gred_offload_dump_stats(sch)) 78262306a36Sopenharmony_ci goto nla_put_failure; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci opts = nla_nest_start_noflag(skb, TCA_OPTIONS); 78562306a36Sopenharmony_ci if (opts == NULL) 78662306a36Sopenharmony_ci goto nla_put_failure; 78762306a36Sopenharmony_ci if (nla_put(skb, TCA_GRED_DPS, sizeof(sopt), &sopt)) 78862306a36Sopenharmony_ci goto nla_put_failure; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci for (i = 0; i < MAX_DPs; i++) { 79162306a36Sopenharmony_ci struct gred_sched_data *q = table->tab[i]; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci max_p[i] = q ? q->parms.max_P : 0; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci if (nla_put(skb, TCA_GRED_MAX_P, sizeof(max_p), max_p)) 79662306a36Sopenharmony_ci goto nla_put_failure; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_LIMIT, sch->limit)) 79962306a36Sopenharmony_ci goto nla_put_failure; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Old style all-in-one dump of VQs */ 80262306a36Sopenharmony_ci parms = nla_nest_start_noflag(skb, TCA_GRED_PARMS); 80362306a36Sopenharmony_ci if (parms == NULL) 80462306a36Sopenharmony_ci goto nla_put_failure; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci for (i = 0; i < MAX_DPs; i++) { 80762306a36Sopenharmony_ci struct gred_sched_data *q = table->tab[i]; 80862306a36Sopenharmony_ci struct tc_gred_qopt opt; 80962306a36Sopenharmony_ci unsigned long qavg; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci memset(&opt, 0, sizeof(opt)); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (!q) { 81462306a36Sopenharmony_ci /* hack -- fix at some point with proper message 81562306a36Sopenharmony_ci This is how we indicate to tc that there is no VQ 81662306a36Sopenharmony_ci at this DP */ 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci opt.DP = MAX_DPs + i; 81962306a36Sopenharmony_ci goto append_opt; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci opt.limit = q->limit; 82362306a36Sopenharmony_ci opt.DP = q->DP; 82462306a36Sopenharmony_ci opt.backlog = gred_backlog(table, q, sch); 82562306a36Sopenharmony_ci opt.prio = q->prio; 82662306a36Sopenharmony_ci opt.qth_min = q->parms.qth_min >> q->parms.Wlog; 82762306a36Sopenharmony_ci opt.qth_max = q->parms.qth_max >> q->parms.Wlog; 82862306a36Sopenharmony_ci opt.Wlog = q->parms.Wlog; 82962306a36Sopenharmony_ci opt.Plog = q->parms.Plog; 83062306a36Sopenharmony_ci opt.Scell_log = q->parms.Scell_log; 83162306a36Sopenharmony_ci opt.early = q->stats.prob_drop; 83262306a36Sopenharmony_ci opt.forced = q->stats.forced_drop; 83362306a36Sopenharmony_ci opt.pdrop = q->stats.pdrop; 83462306a36Sopenharmony_ci opt.packets = q->packetsin; 83562306a36Sopenharmony_ci opt.bytesin = q->bytesin; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (gred_wred_mode(table)) 83862306a36Sopenharmony_ci gred_load_wred_set(table, q); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci qavg = red_calc_qavg(&q->parms, &q->vars, 84162306a36Sopenharmony_ci q->vars.qavg >> q->parms.Wlog); 84262306a36Sopenharmony_ci opt.qave = qavg >> q->parms.Wlog; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ciappend_opt: 84562306a36Sopenharmony_ci if (nla_append(skb, sizeof(opt), &opt) < 0) 84662306a36Sopenharmony_ci goto nla_put_failure; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci nla_nest_end(skb, parms); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* Dump the VQs again, in more structured way */ 85262306a36Sopenharmony_ci vqs = nla_nest_start_noflag(skb, TCA_GRED_VQ_LIST); 85362306a36Sopenharmony_ci if (!vqs) 85462306a36Sopenharmony_ci goto nla_put_failure; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci for (i = 0; i < MAX_DPs; i++) { 85762306a36Sopenharmony_ci struct gred_sched_data *q = table->tab[i]; 85862306a36Sopenharmony_ci struct nlattr *vq; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (!q) 86162306a36Sopenharmony_ci continue; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci vq = nla_nest_start_noflag(skb, TCA_GRED_VQ_ENTRY); 86462306a36Sopenharmony_ci if (!vq) 86562306a36Sopenharmony_ci goto nla_put_failure; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_DP, q->DP)) 86862306a36Sopenharmony_ci goto nla_put_failure; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_FLAGS, q->red_flags)) 87162306a36Sopenharmony_ci goto nla_put_failure; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Stats */ 87462306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, TCA_GRED_VQ_STAT_BYTES, q->bytesin, 87562306a36Sopenharmony_ci TCA_GRED_VQ_PAD)) 87662306a36Sopenharmony_ci goto nla_put_failure; 87762306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PACKETS, q->packetsin)) 87862306a36Sopenharmony_ci goto nla_put_failure; 87962306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_STAT_BACKLOG, 88062306a36Sopenharmony_ci gred_backlog(table, q, sch))) 88162306a36Sopenharmony_ci goto nla_put_failure; 88262306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PROB_DROP, 88362306a36Sopenharmony_ci q->stats.prob_drop)) 88462306a36Sopenharmony_ci goto nla_put_failure; 88562306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PROB_MARK, 88662306a36Sopenharmony_ci q->stats.prob_mark)) 88762306a36Sopenharmony_ci goto nla_put_failure; 88862306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_STAT_FORCED_DROP, 88962306a36Sopenharmony_ci q->stats.forced_drop)) 89062306a36Sopenharmony_ci goto nla_put_failure; 89162306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_STAT_FORCED_MARK, 89262306a36Sopenharmony_ci q->stats.forced_mark)) 89362306a36Sopenharmony_ci goto nla_put_failure; 89462306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_GRED_VQ_STAT_PDROP, q->stats.pdrop)) 89562306a36Sopenharmony_ci goto nla_put_failure; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci nla_nest_end(skb, vq); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci nla_nest_end(skb, vqs); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci return nla_nest_end(skb, opts); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cinla_put_failure: 90462306a36Sopenharmony_ci nla_nest_cancel(skb, opts); 90562306a36Sopenharmony_ci return -EMSGSIZE; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic void gred_destroy(struct Qdisc *sch) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct gred_sched *table = qdisc_priv(sch); 91162306a36Sopenharmony_ci int i; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci for (i = 0; i < table->DPs; i++) 91462306a36Sopenharmony_ci gred_destroy_vq(table->tab[i]); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci gred_offload(sch, TC_GRED_DESTROY); 91762306a36Sopenharmony_ci kfree(table->opt); 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic struct Qdisc_ops gred_qdisc_ops __read_mostly = { 92162306a36Sopenharmony_ci .id = "gred", 92262306a36Sopenharmony_ci .priv_size = sizeof(struct gred_sched), 92362306a36Sopenharmony_ci .enqueue = gred_enqueue, 92462306a36Sopenharmony_ci .dequeue = gred_dequeue, 92562306a36Sopenharmony_ci .peek = qdisc_peek_head, 92662306a36Sopenharmony_ci .init = gred_init, 92762306a36Sopenharmony_ci .reset = gred_reset, 92862306a36Sopenharmony_ci .destroy = gred_destroy, 92962306a36Sopenharmony_ci .change = gred_change, 93062306a36Sopenharmony_ci .dump = gred_dump, 93162306a36Sopenharmony_ci .owner = THIS_MODULE, 93262306a36Sopenharmony_ci}; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic int __init gred_module_init(void) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci return register_qdisc(&gred_qdisc_ops); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic void __exit gred_module_exit(void) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci unregister_qdisc(&gred_qdisc_ops); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cimodule_init(gred_module_init) 94562306a36Sopenharmony_cimodule_exit(gred_module_exit) 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 948