1/* 2 ************************************************************************** 3 * Copyright (c) 2014, 2015 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 19#define NSS_WRED_SUPPORT_TRAFFIC_CLASS 6 20#define NSS_WRED_MAX_TRAFFIC_CLASS NSS_WRED_SUPPORT_TRAFFIC_CLASS+1 21 22/* 23 * nsswred traffic class structure 24 */ 25struct nss_wred_traffic_class { 26 u32 limit; /* Queue length */ 27 u32 weight_mode_value; /* Weight mode value */ 28 struct tc_red_alg_parameter rap;/* Parameters for RED alg */ 29}; 30 31/* 32 * nsswred private qdisc structure 33 */ 34struct nss_wred_sched_data { 35 struct nss_qdisc nq; /* Common base class for all nss qdiscs */ 36 u32 traffic_classes; /* # of traffic classs in this wred*/ 37 u32 def_traffic_class; /* Default traffic class if no match */ 38 tc_nsswred_weight_mode_t weight_mode; /* Weight mode */ 39 struct nss_wred_traffic_class nwtc[NSS_WRED_MAX_TRAFFIC_CLASS]; 40 /* Parameters for each traffic class */ 41 u8 ecn; /* Mark ECN or drop pkt */ 42 u8 weighted; /* This is a wred or red */ 43}; 44 45/* 46 * nss_wred_enqueue() 47 * Enqueue API for nsswred qdisc 48 */ 49static int nss_wred_enqueue(struct sk_buff *skb, struct Qdisc *sch) 50{ 51 return nss_qdisc_enqueue(skb, sch); 52} 53 54/* 55 * nss_wred_dequeue() 56 * Dequeue API for nsswred qdisc 57 */ 58static struct sk_buff *nss_wred_dequeue(struct Qdisc *sch) 59{ 60 return nss_qdisc_dequeue(sch); 61} 62 63/* 64 * nss_wred_drop() 65 * Drops a packet from HLOS queue. 66 */ 67static unsigned int nss_wred_drop(struct Qdisc *sch) 68{ 69 nss_qdisc_info("nsswred dropping"); 70 return nss_qdisc_drop(sch); 71} 72 73/* 74 * nss_wred_reset() 75 * Reset the nsswred qdisc 76 */ 77static void nss_wred_reset(struct Qdisc *sch) 78{ 79 nss_qdisc_info("nsswred resetting!"); 80 nss_qdisc_reset(sch); 81} 82 83/* 84 * nss_wred_destroy() 85 * Destroy the nsswred qdisc 86 */ 87static void nss_wred_destroy(struct Qdisc *sch) 88{ 89 struct nss_qdisc *nq = (struct nss_qdisc *)qdisc_priv(sch); 90 91 /* 92 * Stop the polling of basic stats 93 */ 94 nss_qdisc_stop_basic_stats_polling(nq); 95 96 nss_qdisc_destroy(nq); 97 nss_qdisc_info("nsswred destroyed"); 98} 99 100/* 101 * nsswred policy structure 102 */ 103static const struct nla_policy nss_wred_policy[TCA_NSSWRED_MAX + 1] = { 104 [TCA_NSSWRED_PARMS] = { .len = sizeof(struct tc_nsswred_qopt) }, 105}; 106 107/* 108 * nss_wred_change() 109 * Function call to configure the nsswred parameters 110 */ 111static int nss_wred_change(struct Qdisc *sch, struct nlattr *opt) 112{ 113 struct nss_wred_sched_data *q; 114 struct nlattr *na[TCA_NSSWRED_MAX + 1]; 115 struct tc_nsswred_qopt *qopt; 116 int err; 117 struct nss_if_msg nim; 118 119 q = qdisc_priv(sch); 120 121 if (opt == NULL) { 122 return -EINVAL; 123 } 124 err = nla_parse_nested(na, TCA_NSSWRED_MAX, opt, nss_wred_policy); 125 if (err < 0) { 126 return err; 127 } 128 if (na[TCA_NSSWRED_PARMS] == NULL) { 129 return -EINVAL; 130 } 131 qopt = nla_data(na[TCA_NSSWRED_PARMS]); 132 133 nss_qdisc_info("%s: nsswred %x traffic_classes:%d def_traffic_class: %d Weight_Mode:%d ECN:%d\n", 134 __func__, sch->handle, qopt->traffic_classes, qopt->def_traffic_class, qopt->weight_mode, qopt->ecn); 135 136 if (qopt->traffic_classes) { 137 /* 138 * This is a wred setup command, do checks again because parameters might not come from tc utility 139 */ 140 if (qopt->traffic_classes > NSS_WRED_SUPPORT_TRAFFIC_CLASS) { 141 nss_qdisc_error("%s: nsswred %x traffic classes should not exceeds %d\n", __func__, sch->handle, NSS_WRED_SUPPORT_TRAFFIC_CLASS); 142 return -EINVAL; 143 } 144 if (qopt->def_traffic_class < 1 || qopt->def_traffic_class > qopt->traffic_classes) { 145 nss_qdisc_error("%s: nsswred %x invalid default traffic\n", __func__, sch->handle); 146 return -EINVAL; 147 } 148 if (qopt->weight_mode >= TC_NSSWRED_WEIGHT_MODES) { 149 nss_qdisc_error("%s: nsswred %x invalid weight_mode\n", __func__, sch->handle); 150 return -EINVAL; 151 } 152 q->traffic_classes = qopt->traffic_classes ; 153 q->def_traffic_class = qopt->def_traffic_class; 154 q->weight_mode = qopt->weight_mode; 155 q->ecn = qopt->ecn; 156 q->weighted = 1; 157 } else { 158 if (qopt->traffic_id) { 159 /* 160 * This is a wred traffic class command 161 */ 162 if (!q->traffic_classes) { 163 nss_qdisc_error("%s: nsswred %x not setup yet, can't accept traffic class configuration\n", __func__, sch->handle); 164 return -EINVAL; 165 } 166 if (!qopt->limit || !qopt->rap.min || !qopt->rap.max || !qopt->weight_mode_value || !qopt->rap.exp_weight_factor) { 167 nss_qdisc_error("%s: nsswred %x Requires RED algorithm parameters and weight_mode_value\n", __func__, sch->handle); 168 return -EINVAL; 169 } 170 } else { 171 /* 172 * This is a red setup command 173 */ 174 if (!qopt->limit || !qopt->rap.exp_weight_factor) { 175 nss_qdisc_error("%s: nsswred %x Requires RED algorithm parameters\n", __func__, sch->handle); 176 return -EINVAL; 177 } 178 /* 179 * If min/max does not specify, calculated it 180 */ 181 if (!qopt->rap.max) { 182 qopt->rap.max = qopt->rap.min ? qopt->rap.min * 3 : qopt->limit / 4; 183 } 184 if (!qopt->rap.min) { 185 qopt->rap.min = qopt->rap.max / 3; 186 } 187 q->ecn = qopt->ecn; 188 } 189 q->nwtc[qopt->traffic_id].limit = qopt->limit; 190 q->nwtc[qopt->traffic_id].weight_mode_value = qopt->weight_mode_value; 191 q->nwtc[qopt->traffic_id].rap.min = qopt->rap.min; 192 q->nwtc[qopt->traffic_id].rap.max = qopt->rap.max; 193 q->nwtc[qopt->traffic_id].rap.probability = qopt->rap.probability; 194 q->nwtc[qopt->traffic_id].rap.exp_weight_factor = qopt->rap.exp_weight_factor; 195 } 196 197 nim.msg.shaper_configure.config.msg.shaper_node_config.qos_tag = q->nq.qos_tag; 198 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.limit = qopt->limit; 199 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.weight_mode = qopt->weight_mode; 200 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.weight_mode_value = qopt->weight_mode_value; 201 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.rap.min = qopt->rap.min; 202 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.rap.max = qopt->rap.max; 203 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.rap.probability = qopt->rap.probability; 204 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.rap.exp_weight_factor = qopt->rap.exp_weight_factor; 205 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.traffic_classes = qopt->traffic_classes; 206 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.ecn = qopt->ecn; 207 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.def_traffic_class = qopt->def_traffic_class; 208 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.wred_param.traffic_id = qopt->traffic_id; 209 210 if (nss_qdisc_configure(&q->nq, &nim, NSS_SHAPER_CONFIG_TYPE_SHAPER_NODE_CHANGE_PARAM) < 0) { 211 nss_qdisc_error("%s: nsswred %x configuration failed\n", __func__, sch->handle); 212 return -EINVAL; 213 } 214 215 if (qopt->set_default == 0) 216 return 0; 217 218 /* 219 * Set this qdisc to be the default qdisc for enqueuing packets. 220 */ 221 if (nss_qdisc_set_default(&q->nq) < 0) { 222 nss_qdisc_error("%s: nsswred %x set_default failed\n", __func__, sch->handle); 223 return -EINVAL; 224 } 225 226 nss_qdisc_info("%s: nsswred queue (qos_tag:%u) set as default\n", __func__, q->nq.qos_tag); 227 228 return 0; 229} 230 231/* 232 * nss_wred_init() 233 * Init the nsswred qdisc 234 */ 235static int nss_wred_init(struct Qdisc *sch, struct nlattr *opt) 236{ 237 struct nss_qdisc *nq = qdisc_priv(sch); 238 239 if (opt == NULL) 240 return -EINVAL; 241 242 nss_qdisc_info("Initializing Wred - type %d\n", NSS_SHAPER_NODE_TYPE_WRED); 243 nss_wred_reset(sch); 244 245 if (nss_qdisc_init(sch, nq, NSS_SHAPER_NODE_TYPE_WRED, 0) < 0) 246 return -EINVAL; 247 248 nss_qdisc_info("NSS wred initialized - handle %x parent %x\n", sch->handle, sch->parent); 249 if (nss_wred_change(sch, opt) < 0) { 250 nss_qdisc_destroy(nq); 251 return -EINVAL; 252 } 253 254 /* 255 * Start the stats polling timer 256 */ 257 nss_qdisc_start_basic_stats_polling(nq); 258 259 return 0; 260} 261 262/* 263 * nss_wred_dump() 264 * Dump the parameters of nsswred to tc 265 */ 266static int nss_wred_dump(struct Qdisc *sch, struct sk_buff *skb) 267{ 268 struct nss_wred_sched_data *q; 269 struct nlattr *opts = NULL; 270 struct tc_nsswred_qopt opt; 271 int i; 272 nss_qdisc_info("Nsswred Dumping!"); 273 274 q = qdisc_priv(sch); 275 if (q == NULL) { 276 return -1; 277 } 278 279 if (q->weighted) { 280 opt.traffic_classes = q->traffic_classes; 281 opt.def_traffic_class = q->def_traffic_class; 282 opt.ecn = q->ecn; 283 opt.weight_mode = q->weight_mode; 284 for (i = 0 ; i < q->traffic_classes; i++) { 285 opt.tntc[i].limit = q->nwtc[i+1].limit; 286 opt.tntc[i].weight_mode_value = q->nwtc[i+1].weight_mode_value; 287 opt.tntc[i].rap.exp_weight_factor = q->nwtc[i+1].rap.exp_weight_factor; 288 opt.tntc[i].rap.min = q->nwtc[i+1].rap.min; 289 opt.tntc[i].rap.max = q->nwtc[i+1].rap.max; 290 opt.tntc[i].rap.probability = q->nwtc[i+1].rap.probability; 291 } 292 } else { 293 opt.ecn = q->ecn; 294 opt.limit = q->nwtc[0].limit; 295 opt.rap.min = q->nwtc[0].rap.min; 296 opt.rap.max = q->nwtc[0].rap.max; 297 opt.rap.exp_weight_factor = q->nwtc[0].rap.exp_weight_factor; 298 opt.rap.probability = q->nwtc[0].rap.probability; 299 } 300 301 opts = nla_nest_start(skb, TCA_OPTIONS); 302 if (opts == NULL || nla_put(skb, TCA_NSSWRED_PARMS, sizeof(opt), &opt)) { 303 goto nla_put_failure; 304 } 305 306 return nla_nest_end(skb, opts); 307 308nla_put_failure: 309 nla_nest_cancel(skb, opts); 310 return -EMSGSIZE; 311} 312 313/* 314 * nss_wred_peek() 315 * Peeks the first packet in queue for this qdisc 316 */ 317static struct sk_buff *nss_wred_peek(struct Qdisc *sch) 318{ 319 nss_qdisc_info("Nsswred Peeking"); 320 return nss_qdisc_peek(sch); 321} 322 323/* 324 * Registration structure for nss_red qdisc 325 */ 326struct Qdisc_ops nss_red_qdisc_ops __read_mostly = { 327 .id = "nssred", 328 .priv_size = sizeof(struct nss_wred_sched_data), 329 .enqueue = nss_wred_enqueue, 330 .dequeue = nss_wred_dequeue, 331 .peek = nss_wred_peek, 332 .drop = nss_wred_drop, 333 .init = nss_wred_init, 334 .reset = nss_wred_reset, 335 .destroy = nss_wred_destroy, 336 .change = nss_wred_change, 337 .dump = nss_wred_dump, 338 .owner = THIS_MODULE, 339}; 340 341/* 342 * Registration structure for nss_wred qdisc 343 */ 344struct Qdisc_ops nss_wred_qdisc_ops __read_mostly = { 345 .id = "nsswred", 346 .priv_size = sizeof(struct nss_wred_sched_data), 347 .enqueue = nss_wred_enqueue, 348 .dequeue = nss_wred_dequeue, 349 .peek = nss_wred_peek, 350 .drop = nss_wred_drop, 351 .init = nss_wred_init, 352 .reset = nss_wred_reset, 353 .destroy = nss_wred_destroy, 354 .change = nss_wred_change, 355 .dump = nss_wred_dump, 356 .owner = THIS_MODULE, 357}; 358