1/* 2 ************************************************************************** 3 * Copyright (c) 2014, The Linux Foundation. All rights reserved. 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all copies. 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 ************************************************************************** 15 */ 16 17#include "nss_qdisc.h" 18 19struct nss_codel_stats { 20 u32 peak_queue_delay; /* Peak delay experienced by a dequeued packet */ 21 u32 peak_drop_delay; /* Peak delay experienced by a packet that is dropped */ 22}; 23 24struct nss_codel_sched_data { 25 struct nss_qdisc nq; /* Common base class for all nss qdiscs */ 26 u32 target; /* Acceptable value of queue delay */ 27 u32 limit; /* Length of queue */ 28 u32 interval; /* Monitoring interval */ 29 u8 set_default; /* Flag to set qdisc as default qdisc for enqueue */ 30 struct nss_codel_stats stats; /* Contains nss_codel related stats */ 31}; 32 33static int nss_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch) 34{ 35 return nss_qdisc_enqueue(skb, sch); 36} 37 38static struct sk_buff *nss_codel_dequeue(struct Qdisc *sch) 39{ 40 return nss_qdisc_dequeue(sch); 41} 42 43static unsigned int nss_codel_drop(struct Qdisc *sch) 44{ 45 return nss_qdisc_drop(sch); 46} 47 48static void nss_codel_reset(struct Qdisc *sch) 49{ 50 nss_qdisc_info("nss_codel resetting!"); 51 nss_qdisc_reset(sch); 52} 53 54static void nss_codel_destroy(struct Qdisc *sch) 55{ 56 struct nss_qdisc *nq = qdisc_priv(sch); 57 /* 58 * Stop the polling of basic stats 59 */ 60 nss_qdisc_stop_basic_stats_polling(nq); 61 nss_qdisc_destroy(nq); 62 nss_qdisc_info("nss_codel destroyed"); 63} 64 65static const struct nla_policy nss_codel_policy[TCA_NSSCODEL_MAX + 1] = { 66 [TCA_NSSCODEL_PARMS] = { .len = sizeof(struct tc_nsscodel_qopt) }, 67}; 68 69static int nss_codel_change(struct Qdisc *sch, struct nlattr *opt) 70{ 71 struct nss_codel_sched_data *q; 72 struct nlattr *na[TCA_NSSCODEL_MAX + 1]; 73 struct tc_nsscodel_qopt *qopt; 74 struct nss_if_msg nim; 75 int err; 76 struct net_device *dev = qdisc_dev(sch); 77 78 q = qdisc_priv(sch); 79 80 if (opt == NULL) 81 return -EINVAL; 82 83 err = nla_parse_nested(na, TCA_NSSCODEL_MAX, opt, nss_codel_policy); 84 if (err < 0) 85 return err; 86 87 if (na[TCA_NSSCODEL_PARMS] == NULL) 88 return -EINVAL; 89 90 qopt = nla_data(na[TCA_NSSCODEL_PARMS]); 91 92 if (!qopt->target || !qopt->interval) { 93 nss_qdisc_error("nss_codel requires a non-zero value for target " 94 "and interval\n"); 95 return -EINVAL; 96 } 97 98 if (!qopt->limit) 99 qopt->limit = dev->tx_queue_len ? : 1; 100 101 q->target = qopt->target; 102 q->limit = qopt->limit; 103 q->interval = qopt->interval; 104 q->set_default = qopt->set_default; 105 106 /* 107 * Required for basic stats display 108 */ 109 sch->limit = qopt->limit; 110 111 nss_qdisc_info("Target:%u Limit:%u Interval:%u set_default = %u\n", 112 q->target, q->limit, q->interval, qopt->set_default); 113 114 115 nim.msg.shaper_configure.config.msg.shaper_node_config.qos_tag = q->nq.qos_tag; 116 /* 117 * Target and interval time needs to be provided in milliseconds 118 * (tc provides us the time in mircoseconds and therefore we divide by 1000) 119 */ 120 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.codel_param.qlen_max = q->limit; 121 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.codel_param.cap.interval = q->interval/1000; 122 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.codel_param.cap.target = q->target/1000; 123 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.codel_param.cap.mtu = psched_mtu(dev); 124 nss_qdisc_info("%s: MTU size of interface %s is %u bytes\n", __func__, dev->name, 125 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.codel_param.cap.mtu); 126 127 if (nss_qdisc_configure(&q->nq, &nim, 128 NSS_SHAPER_CONFIG_TYPE_CODEL_CHANGE_PARAM) < 0) { 129 return -EINVAL; 130 } 131 132 /* 133 * There is nothing we need to do if the qdisc is not 134 * set as default qdisc. 135 */ 136 if (!q->set_default) 137 return 0; 138 139 /* 140 * Set this qdisc to be the default qdisc for enqueuing packets. 141 */ 142 if (nss_qdisc_set_default(&q->nq) < 0) 143 return -EINVAL; 144 145 return 0; 146} 147 148static int nss_codel_init(struct Qdisc *sch, struct nlattr *opt) 149{ 150 struct nss_qdisc *nq = qdisc_priv(sch); 151 152 if (opt == NULL) 153 return -EINVAL; 154 155 nss_codel_reset(sch); 156 if (nss_qdisc_init(sch, nq, NSS_SHAPER_NODE_TYPE_CODEL, 0) < 0) 157 return -EINVAL; 158 159 if (nss_codel_change(sch, opt) < 0) { 160 nss_qdisc_destroy(nq); 161 return -EINVAL; 162 } 163 164 /* 165 * Start the stats polling timer 166 */ 167 nss_qdisc_start_basic_stats_polling(nq); 168 169 return 0; 170} 171 172static int nss_codel_dump(struct Qdisc *sch, struct sk_buff *skb) 173{ 174 struct nss_codel_sched_data *q; 175 struct nlattr *opts = NULL; 176 struct tc_nsscodel_qopt opt; 177 178 nss_qdisc_info("NssCodel Dumping!"); 179 180 q = qdisc_priv(sch); 181 if (q == NULL) { 182 return -1; 183 } 184 185 opt.target = q->target; 186 opt.limit = q->limit; 187 opt.interval = q->interval; 188 opt.set_default = q->set_default; 189 opts = nla_nest_start(skb, TCA_OPTIONS); 190 if (opts == NULL) { 191 goto nla_put_failure; 192 } 193 if (nla_put(skb, TCA_NSSCODEL_PARMS, sizeof(opt), &opt)) 194 goto nla_put_failure; 195 196 return nla_nest_end(skb, opts); 197 198nla_put_failure: 199 nla_nest_cancel(skb, opts); 200 return -EMSGSIZE; 201} 202 203static int nss_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d) 204{ 205 struct nss_codel_sched_data *q = qdisc_priv(sch); 206 struct tc_nsscodel_xstats st = { 207 .peak_queue_delay = q->nq.basic_stats_latest.packet_latency_peak_msec_dequeued, 208 .peak_drop_delay = q->nq.basic_stats_latest.packet_latency_peak_msec_dropped, 209 }; 210 211 return gnet_stats_copy_app(d, &st, sizeof(st)); 212} 213 214static struct sk_buff *nss_codel_peek(struct Qdisc *sch) 215{ 216 nss_qdisc_info("Nsscodel Peeking"); 217 return nss_qdisc_peek(sch); 218} 219 220 221struct Qdisc_ops nss_codel_qdisc_ops __read_mostly = { 222 .id = "nsscodel", 223 .priv_size = sizeof(struct nss_codel_sched_data), 224 .enqueue = nss_codel_enqueue, 225 .dequeue = nss_codel_dequeue, 226 .peek = nss_codel_peek, 227 .drop = nss_codel_drop, 228 .init = nss_codel_init, 229 .reset = nss_codel_reset, 230 .destroy = nss_codel_destroy, 231 .change = nss_codel_change, 232 .dump = nss_codel_dump, 233 .dump_stats = nss_codel_dump_stats, 234 .owner = THIS_MODULE, 235}; 236