1/* 2 * lib/route/sch/sfq.c SFQ Qdisc 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> 10 */ 11 12/** 13 * @ingroup qdisc_api 14 * @defgroup sfq Stochastic Fairness Queueing (SFQ) 15 * @brief 16 * 17 * @par Parameter Description 18 * - \b Quantum: Number of bytes to send out per slot and round. 19 * - \b Perturbation: Timer period between changing the hash function. 20 * - \b Limit: Upper limit of queue in number of packets before SFQ starts 21 * dropping packets. 22 * - \b Divisor: Hash table divisor, i.e. size of hash table. 23 * @{ 24 */ 25 26#include <netlink-local.h> 27#include <netlink-tc.h> 28#include <netlink/netlink.h> 29#include <netlink/utils.h> 30#include <netlink/route/qdisc.h> 31#include <netlink/route/qdisc-modules.h> 32#include <netlink/route/sch/sfq.h> 33 34/** @cond SKIP */ 35#define SCH_SFQ_ATTR_QUANTUM 0x01 36#define SCH_SFQ_ATTR_PERTURB 0x02 37#define SCH_SFQ_ATTR_LIMIT 0x04 38#define SCH_SFQ_ATTR_DIVISOR 0x08 39#define SCH_SFQ_ATTR_FLOWS 0x10 40/** @endcond */ 41 42static inline struct rtnl_sfq *sfq_qdisc(struct rtnl_qdisc *qdisc) 43{ 44 return (struct rtnl_sfq *) qdisc->q_subdata; 45} 46 47static inline struct rtnl_sfq *sfq_alloc(struct rtnl_qdisc *qdisc) 48{ 49 if (!qdisc->q_subdata) 50 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_sfq)); 51 52 return sfq_qdisc(qdisc); 53} 54 55static int sfq_msg_parser(struct rtnl_qdisc *qdisc) 56{ 57 struct rtnl_sfq *sfq; 58 struct tc_sfq_qopt *opts; 59 60 if (!(qdisc->ce_mask & TCA_ATTR_OPTS)) 61 return 0; 62 63 if (qdisc->q_opts->d_size < sizeof(*opts)) 64 return -NLE_INVAL; 65 66 sfq = sfq_alloc(qdisc); 67 if (!sfq) 68 return -NLE_NOMEM; 69 70 opts = (struct tc_sfq_qopt *) qdisc->q_opts->d_data; 71 72 sfq->qs_quantum = opts->quantum; 73 sfq->qs_perturb = opts->perturb_period; 74 sfq->qs_limit = opts->limit; 75 sfq->qs_divisor = opts->divisor; 76 sfq->qs_flows = opts->flows; 77 78 sfq->qs_mask = (SCH_SFQ_ATTR_QUANTUM | SCH_SFQ_ATTR_PERTURB | 79 SCH_SFQ_ATTR_LIMIT | SCH_SFQ_ATTR_DIVISOR | 80 SCH_SFQ_ATTR_FLOWS); 81 82 return 0; 83} 84 85static void sfq_free_data(struct rtnl_qdisc *qdisc) 86{ 87 free(qdisc->q_subdata); 88} 89 90static void sfq_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) 91{ 92 struct rtnl_sfq *sfq = sfq_qdisc(qdisc); 93 94 if (sfq) 95 nl_dump(p, " quantum %u perturb %us", sfq->qs_quantum, 96 nl_ticks2us(sfq->qs_perturb * nl_get_hz())); 97} 98 99static void sfq_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) 100{ 101 struct rtnl_sfq *sfq = sfq_qdisc(qdisc); 102 103 if (sfq) 104 nl_dump(p, "limit %u divisor %u", 105 sfq->qs_limit, sfq->qs_divisor); 106} 107 108static struct nl_msg *sfq_get_opts(struct rtnl_qdisc *qdisc) 109{ 110 struct rtnl_sfq *sfq; 111 struct tc_sfq_qopt opts; 112 struct nl_msg *msg; 113 114 sfq = sfq_qdisc(qdisc); 115 if (!sfq) 116 return NULL; 117 118 msg = nlmsg_alloc(); 119 if (!msg) 120 goto errout; 121 122 memset(&opts, 0, sizeof(opts)); 123 opts.quantum = sfq->qs_quantum; 124 opts.perturb_period = sfq->qs_perturb; 125 opts.limit = sfq->qs_limit; 126 127 if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) 128 goto errout; 129 130 return msg; 131errout: 132 nlmsg_free(msg); 133 return NULL; 134} 135 136/** 137 * @name Attribute Access 138 * @{ 139 */ 140 141/** 142 * Set quantum of SFQ qdisc. 143 * @arg qdisc SFQ qdisc to be modified. 144 * @arg quantum New quantum in bytes. 145 * @return 0 on success or a negative error code. 146 */ 147int rtnl_sfq_set_quantum(struct rtnl_qdisc *qdisc, int quantum) 148{ 149 struct rtnl_sfq *sfq; 150 151 sfq = sfq_alloc(qdisc); 152 if (!sfq) 153 return -NLE_NOMEM; 154 155 sfq->qs_quantum = quantum; 156 sfq->qs_mask |= SCH_SFQ_ATTR_QUANTUM; 157 158 return 0; 159} 160 161/** 162 * Get quantum of SFQ qdisc. 163 * @arg qdisc SFQ qdisc. 164 * @return Quantum in bytes or a negative error code. 165 */ 166int rtnl_sfq_get_quantum(struct rtnl_qdisc *qdisc) 167{ 168 struct rtnl_sfq *sfq; 169 170 sfq = sfq_qdisc(qdisc); 171 if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM) 172 return sfq->qs_quantum; 173 else 174 return -NLE_NOATTR; 175} 176 177/** 178 * Set limit of SFQ qdisc. 179 * @arg qdisc SFQ qdisc to be modified. 180 * @arg limit New limit in number of packets. 181 * @return 0 on success or a negative error code. 182 */ 183int rtnl_sfq_set_limit(struct rtnl_qdisc *qdisc, int limit) 184{ 185 struct rtnl_sfq *sfq; 186 187 sfq = sfq_alloc(qdisc); 188 if (!sfq) 189 return -NLE_NOMEM; 190 191 sfq->qs_limit = limit; 192 sfq->qs_mask |= SCH_SFQ_ATTR_LIMIT; 193 194 return 0; 195} 196 197/** 198 * Get limit of SFQ qdisc. 199 * @arg qdisc SFQ qdisc. 200 * @return Limit or a negative error code. 201 */ 202int rtnl_sfq_get_limit(struct rtnl_qdisc *qdisc) 203{ 204 struct rtnl_sfq *sfq; 205 206 sfq = sfq_qdisc(qdisc); 207 if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_LIMIT) 208 return sfq->qs_limit; 209 else 210 return -NLE_NOATTR; 211} 212 213/** 214 * Set perturbation interval of SFQ qdisc. 215 * @arg qdisc SFQ qdisc to be modified. 216 * @arg perturb New perturbation interval in seconds. 217 * @note A value of 0 disables perturbation altogether. 218 * @return 0 on success or a negative error code. 219 */ 220int rtnl_sfq_set_perturb(struct rtnl_qdisc *qdisc, int perturb) 221{ 222 struct rtnl_sfq *sfq; 223 224 sfq = sfq_alloc(qdisc); 225 if (!sfq) 226 return -NLE_NOMEM; 227 228 sfq->qs_perturb = perturb; 229 sfq->qs_mask |= SCH_SFQ_ATTR_PERTURB; 230 231 return 0; 232} 233 234/** 235 * Get perturbation interval of SFQ qdisc. 236 * @arg qdisc SFQ qdisc. 237 * @return Perturbation interval in seconds or a negative error code. 238 */ 239int rtnl_sfq_get_perturb(struct rtnl_qdisc *qdisc) 240{ 241 struct rtnl_sfq *sfq; 242 243 sfq = sfq_qdisc(qdisc); 244 if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_PERTURB) 245 return sfq->qs_perturb; 246 else 247 return -NLE_NOATTR; 248} 249 250/** 251 * Get divisor of SFQ qdisc. 252 * @arg qdisc SFQ qdisc. 253 * @return Divisor in number of entries or a negative error code. 254 */ 255int rtnl_sfq_get_divisor(struct rtnl_qdisc *qdisc) 256{ 257 struct rtnl_sfq *sfq; 258 259 sfq = sfq_qdisc(qdisc); 260 if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR) 261 return sfq->qs_divisor; 262 else 263 return -NLE_NOATTR; 264} 265 266/** @} */ 267 268static struct rtnl_qdisc_ops sfq_ops = { 269 .qo_kind = "sfq", 270 .qo_msg_parser = sfq_msg_parser, 271 .qo_free_data = sfq_free_data, 272 .qo_dump = { 273 [NL_DUMP_LINE] = sfq_dump_line, 274 [NL_DUMP_DETAILS] = sfq_dump_details, 275 }, 276 .qo_get_opts = sfq_get_opts, 277}; 278 279static void __init sfq_init(void) 280{ 281 rtnl_qdisc_register(&sfq_ops); 282} 283 284static void __exit sfq_exit(void) 285{ 286 rtnl_qdisc_unregister(&sfq_ops); 287} 288 289/** @} */ 290