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 19/* 20 * nss_blackhole private qdisc structure 21 */ 22struct nss_blackhole_sched_data { 23 struct nss_qdisc nq; /* Common base class for all nss qdiscs */ 24 u8 set_default; /* Flag to set qdisc as default qdisc for enqueue */ 25}; 26 27/* 28 * nss_blackhole_enqueue() 29 * Enqueue API for nss blackhole qdisc. 30 */ 31static int nss_blackhole_enqueue(struct sk_buff *skb, struct Qdisc *sch) 32{ 33 return nss_qdisc_enqueue(skb, sch); 34} 35 36/* 37 * nss_blackhole_dequeue() 38 * Dequeue API for nss blackhole qdisc. 39 */ 40static struct sk_buff *nss_blackhole_dequeue(struct Qdisc *sch) 41{ 42 return nss_qdisc_dequeue(sch); 43} 44 45/* 46 * nss_blackhole_drop() 47 * The following function drops a packet from HLOS queue. 48 * 49 * Note, this does not drop packets from queues in the NSS. We do not support that. 50 */ 51static unsigned int nss_blackhole_drop(struct Qdisc *sch) 52{ 53 nss_qdisc_info("%s: qdisc %x dropping\n", __func__, sch->handle); 54 return nss_qdisc_drop(sch); 55} 56 57/* 58 * nss_blackhole_reset() 59 * Resets the nss blackhole qdisc. 60 */ 61static void nss_blackhole_reset(struct Qdisc *sch) 62{ 63 nss_qdisc_info("%s: qdisc %x resetting\n", __func__, sch->handle); 64 nss_qdisc_reset(sch); 65} 66 67/* 68 * nss_blackhole_destroy() 69 * Destroys the nss blackhole qdisc. 70 */ 71static void nss_blackhole_destroy(struct Qdisc *sch) 72{ 73 struct nss_qdisc *nq = (struct nss_qdisc *)qdisc_priv(sch); 74 75 /* 76 * Stop the polling of basic stats 77 */ 78 nss_qdisc_stop_basic_stats_polling(nq); 79 80 nss_qdisc_info("%s: destroying qdisc %x\n", __func__, sch->handle); 81 nss_qdisc_destroy(nq); 82} 83 84/* 85 * nss_blackhole policy structure 86 */ 87static const struct nla_policy nss_blackhole_policy[TCA_NSSBLACKHOLE_MAX + 1] = { 88 [TCA_NSSBLACKHOLE_PARMS] = { .len = sizeof(struct tc_nssblackhole_qopt) }, 89}; 90 91/* 92 * nss_blackhole_change() 93 * Function call used to configure the parameters of the nss blackhole qdisc. 94 */ 95static int nss_blackhole_change(struct Qdisc *sch, struct nlattr *opt) 96{ 97 struct nss_blackhole_sched_data *q; 98 struct nlattr *na[TCA_NSSBLACKHOLE_MAX + 1]; 99 struct tc_nssblackhole_qopt *qopt; 100 int err; 101 struct nss_if_msg nim; 102 103 q = qdisc_priv(sch); 104 105 if (opt == NULL) { 106 return -EINVAL; 107 } 108 109 err = nla_parse_nested(na, TCA_NSSBLACKHOLE_MAX, opt, nss_blackhole_policy); 110 if (err < 0) 111 return err; 112 113 if (na[TCA_NSSBLACKHOLE_PARMS] == NULL) 114 return -EINVAL; 115 116 qopt = nla_data(na[TCA_NSSBLACKHOLE_PARMS]); 117 118 /* 119 * Required for basic stats display 120 */ 121 sch->limit = 0; 122 123 q->set_default = qopt->set_default; 124 nss_qdisc_info("%s: qdisc set_default = %u\n", __func__, qopt->set_default); 125 126 /* 127 * Underneath nss_bloackhole uses a fifo in the NSS. This is why we are sending down a configuration 128 * message to a fifo node. There are no blackhole shaper in the NSS as yet. 129 * 130 * Note: We simply set the limit of fifo to zero to get the blackhole behavior. 131 */ 132 nim.msg.shaper_configure.config.msg.shaper_node_config.qos_tag = q->nq.qos_tag; 133 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.fifo_param.limit = 0; 134 nim.msg.shaper_configure.config.msg.shaper_node_config.snc.fifo_param.drop_mode = NSS_SHAPER_FIFO_DROP_MODE_TAIL; 135 if (nss_qdisc_configure(&q->nq, &nim, NSS_SHAPER_CONFIG_TYPE_FIFO_CHANGE_PARAM) < 0) { 136 nss_qdisc_error("%s: qdisc %x configuration failed\n", __func__, sch->handle); 137 return -EINVAL; 138 } 139 140 /* 141 * There is nothing we need to do if the qdisc is not 142 * set as default qdisc. 143 */ 144 if (q->set_default == 0) 145 return 0; 146 147 /* 148 * Set this qdisc to be the default qdisc for enqueuing packets. 149 */ 150 if (nss_qdisc_set_default(&q->nq) < 0) { 151 nss_qdisc_error("%s: qdisc %x set_default failed\n", __func__, sch->handle); 152 return -EINVAL; 153 } 154 155 nss_qdisc_info("%s: qdisc %x set as default\n", __func__, q->nq.qos_tag); 156 return 0; 157} 158 159/* 160 * nss_blackhole_init() 161 * Initializes a nss blackhole qdisc. 162 */ 163static int nss_blackhole_init(struct Qdisc *sch, struct nlattr *opt) 164{ 165 struct nss_qdisc *nq = qdisc_priv(sch); 166 167 if (opt == NULL) 168 return -EINVAL; 169 170 nss_qdisc_info("%s: qdisc %x initializing\n", __func__, sch->handle); 171 nss_blackhole_reset(sch); 172 173 if (nss_qdisc_init(sch, nq, NSS_SHAPER_NODE_TYPE_FIFO, 0) < 0) 174 return -EINVAL; 175 176 nss_qdisc_info("%s: qdisc %x initialized with parent %x\n", __func__, sch->handle, sch->parent); 177 if (nss_blackhole_change(sch, opt) < 0) { 178 nss_qdisc_destroy(nq); 179 return -EINVAL; 180 } 181 182 /* 183 * Start the stats polling timer 184 */ 185 nss_qdisc_start_basic_stats_polling(nq); 186 187 return 0; 188} 189 190/* 191 * nss_blackhole_dump() 192 * Dumps qdisc parameters for nss blackhole. 193 */ 194static int nss_blackhole_dump(struct Qdisc *sch, struct sk_buff *skb) 195{ 196 struct nss_blackhole_sched_data *q; 197 struct nlattr *opts = NULL; 198 struct tc_nssblackhole_qopt opt; 199 200 nss_qdisc_info("%s: qdisc %x dumping!\n", __func__, sch->handle); 201 202 q = qdisc_priv(sch); 203 if (q == NULL) { 204 return -1; 205 } 206 207 opt.set_default = q->set_default; 208 209 opts = nla_nest_start(skb, TCA_OPTIONS); 210 if (opts == NULL) { 211 goto nla_put_failure; 212 } 213 if (nla_put(skb, TCA_NSSBLACKHOLE_PARMS, sizeof(opt), &opt)) 214 goto nla_put_failure; 215 216 return nla_nest_end(skb, opts); 217 218nla_put_failure: 219 nla_nest_cancel(skb, opts); 220 return -EMSGSIZE; 221} 222 223/* 224 * nss_blackhole_peek() 225 * Peeks the first packet in queue for this qdisc. 226 * 227 * Note: This just peeks at the first packet in what is present in HLOS. This does not 228 * perform an actual peak into the queue in the NSS. Given the async delay between 229 * the processors, there is less use in implementing this function. 230 */ 231static struct sk_buff *nss_blackhole_peek(struct Qdisc *sch) 232{ 233 nss_qdisc_info("%s: qdisc %x peeked\n", __func__, sch->handle); 234 return nss_qdisc_peek(sch); 235} 236 237/* 238 * Registration structure for nss blackhole qdisc 239 */ 240struct Qdisc_ops nss_blackhole_qdisc_ops __read_mostly = { 241 .id = "nssblackhole", 242 .priv_size = sizeof(struct nss_blackhole_sched_data), 243 .enqueue = nss_blackhole_enqueue, 244 .dequeue = nss_blackhole_dequeue, 245 .peek = nss_blackhole_peek, 246 .drop = nss_blackhole_drop, 247 .init = nss_blackhole_init, 248 .reset = nss_blackhole_reset, 249 .destroy = nss_blackhole_destroy, 250 .change = nss_blackhole_change, 251 .dump = nss_blackhole_dump, 252 .owner = THIS_MODULE, 253}; 254