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 (at) 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 42 static inline struct rtnl_sfq *sfq_qdisc(struct rtnl_qdisc *qdisc) 43 { 44 return (struct rtnl_sfq *) qdisc->q_subdata; 45 } 46 47 static 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 55 static 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 85 static void sfq_free_data(struct rtnl_qdisc *qdisc) 86 { 87 free(qdisc->q_subdata); 88 } 89 90 static 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 99 static 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 108 static 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; 131 errout: 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 */ 147 int 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 */ 166 int 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 */ 183 int 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 */ 202 int 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 */ 220 int 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 */ 239 int 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 */ 255 int 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 268 static 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 279 static void __init sfq_init(void) 280 { 281 rtnl_qdisc_register(&sfq_ops); 282 } 283 284 static void __exit sfq_exit(void) 285 { 286 rtnl_qdisc_unregister(&sfq_ops); 287 } 288 289 /** @} */ 290