00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <netlink-local.h>
00019 #include <netlink-tc.h>
00020 #include <netlink/netlink.h>
00021 #include <netlink/cache.h>
00022 #include <netlink/utils.h>
00023 #include <netlink/route/tc-api.h>
00024 #include <netlink/route/qdisc.h>
00025 #include <netlink/route/class.h>
00026 #include <netlink/route/link.h>
00027 #include <netlink/route/qdisc/tbf.h>
00028
00029
00030 #define TBF_ATTR_LIMIT 0x01
00031 #define TBF_ATTR_RATE 0x02
00032 #define TBF_ATTR_PEAKRATE 0x10
00033
00034
00035 static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = {
00036 [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) },
00037 };
00038
00039 static int tbf_msg_parser(struct rtnl_tc *tc, void *data)
00040 {
00041 struct nlattr *tb[TCA_TBF_MAX + 1];
00042 struct rtnl_tbf *tbf = data;
00043 int err;
00044
00045 if ((err = tca_parse(tb, TCA_TBF_MAX, tc, tbf_policy)) < 0)
00046 return err;
00047
00048 if (tb[TCA_TBF_PARMS]) {
00049 struct tc_tbf_qopt opts;
00050 int bufsize;
00051
00052 nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts));
00053 tbf->qt_limit = opts.limit;
00054
00055 rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate);
00056 tbf->qt_rate_txtime = opts.buffer;
00057 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
00058 opts.rate.rate);
00059 tbf->qt_rate_bucket = bufsize;
00060
00061 rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate);
00062 tbf->qt_peakrate_txtime = opts.mtu;
00063 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu),
00064 opts.peakrate.rate);
00065 tbf->qt_peakrate_bucket = bufsize;
00066
00067 rtnl_tc_set_mpu(tc, tbf->qt_rate.rs_mpu);
00068 rtnl_tc_set_overhead(tc, tbf->qt_rate.rs_overhead);
00069
00070 tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_RATE | TBF_ATTR_PEAKRATE);
00071 }
00072
00073 return 0;
00074 }
00075
00076 static void tbf_dump_line(struct rtnl_tc *tc, void *data,
00077 struct nl_dump_params *p)
00078 {
00079 double r, rbit, lim;
00080 char *ru, *rubit, *limu;
00081 struct rtnl_tbf *tbf = data;
00082
00083 if (!tbf)
00084 return;
00085
00086 r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
00087 rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
00088 lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
00089
00090 nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
00091 r, ru, rbit, rubit, lim, limu);
00092 }
00093
00094 static void tbf_dump_details(struct rtnl_tc *tc, void *data,
00095 struct nl_dump_params *p)
00096 {
00097 struct rtnl_tbf *tbf = data;
00098
00099 if (!tbf)
00100 return;
00101
00102 if (1) {
00103 char *bu, *cu;
00104 double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu);
00105 double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
00106 &cu);
00107
00108 nl_dump(p, "rate-bucket-size %1.f%s "
00109 "rate-cell-size %.1f%s\n",
00110 bs, bu, cl, cu);
00111
00112 }
00113
00114 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00115 char *pru, *prbu, *bsu, *clu;
00116 double pr, prb, bs, cl;
00117
00118 pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru);
00119 prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu);
00120 bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
00121 cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
00122 &clu);
00123
00124 nl_dump_line(p, " peak-rate %.2f%s/s (%.0f%s) "
00125 "bucket-size %.1f%s cell-size %.1f%s"
00126 "latency %.1f%s",
00127 pr, pru, prb, prbu, bs, bsu, cl, clu);
00128 }
00129 }
00130
00131 static int tbf_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
00132 {
00133 uint32_t rtab[RTNL_TC_RTABLE_SIZE], ptab[RTNL_TC_RTABLE_SIZE];
00134 struct tc_tbf_qopt opts;
00135 struct rtnl_tbf *tbf = data;
00136 int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
00137
00138 if (!(tbf->qt_mask & required) != required)
00139 return -NLE_MISSING_ATTR;
00140
00141 memset(&opts, 0, sizeof(opts));
00142 opts.limit = tbf->qt_limit;
00143 opts.buffer = tbf->qt_rate_txtime;
00144
00145 rtnl_tc_build_rate_table(tc, &tbf->qt_rate, rtab);
00146 rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
00147
00148 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00149 opts.mtu = tbf->qt_peakrate_txtime;
00150 rtnl_tc_build_rate_table(tc, &tbf->qt_peakrate, ptab);
00151 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
00152
00153 }
00154
00155 NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
00156 NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
00157
00158 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
00159 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
00160
00161 return 0;
00162
00163 nla_put_failure:
00164 return -NLE_MSGSIZE;
00165 }
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178 void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
00179 {
00180 struct rtnl_tbf *tbf;
00181
00182 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00183 BUG();
00184
00185 tbf->qt_limit = limit;
00186 tbf->qt_mask |= TBF_ATTR_LIMIT;
00187 }
00188
00189 static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
00190 int bucket)
00191 {
00192 double limit;
00193
00194 limit = (double) spec->rs_rate * ((double) latency / 1000000.);
00195 limit += bucket;
00196
00197 return limit;
00198 }
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
00219 {
00220 struct rtnl_tbf *tbf;
00221 double limit, limit2;
00222
00223 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00224 BUG();
00225
00226 if (!(tbf->qt_mask & TBF_ATTR_RATE))
00227 return -NLE_MISSING_ATTR;
00228
00229 limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
00230
00231 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00232 limit2 = calc_limit(&tbf->qt_peakrate, latency,
00233 tbf->qt_peakrate_bucket);
00234
00235 if (limit2 < limit)
00236 limit = limit2;
00237 }
00238
00239 rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
00240
00241 return 0;
00242 }
00243
00244
00245
00246
00247
00248
00249 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
00250 {
00251 struct rtnl_tbf *tbf;
00252
00253 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00254 BUG();
00255
00256 if (tbf->qt_mask & TBF_ATTR_LIMIT)
00257 return tbf->qt_limit;
00258 else
00259 return -NLE_NOATTR;
00260 }
00261
00262 static inline int calc_cell_log(int cell, int bucket)
00263 {
00264 cell = rtnl_tc_calc_cell_log(cell);
00265 return cell;
00266 }
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276 void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
00277 int cell)
00278 {
00279 struct rtnl_tbf *tbf;
00280 int cell_log;
00281
00282 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00283 BUG();
00284
00285 if (!cell)
00286 cell_log = UINT8_MAX;
00287 else
00288 cell_log = rtnl_tc_calc_cell_log(cell);
00289
00290 tbf->qt_rate.rs_rate = rate;
00291 tbf->qt_rate_bucket = bucket;
00292 tbf->qt_rate.rs_cell_log = cell_log;
00293 tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate);
00294 tbf->qt_mask |= TBF_ATTR_RATE;
00295 }
00296
00297
00298
00299
00300
00301
00302 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
00303 {
00304 struct rtnl_tbf *tbf;
00305
00306 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00307 BUG();
00308
00309 if (tbf->qt_mask & TBF_ATTR_RATE)
00310 return tbf->qt_rate.rs_rate;
00311 else
00312 return -1;
00313 }
00314
00315
00316
00317
00318
00319
00320 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
00321 {
00322 struct rtnl_tbf *tbf;
00323
00324 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00325 BUG();
00326
00327 if (tbf->qt_mask & TBF_ATTR_RATE)
00328 return tbf->qt_rate_bucket;
00329 else
00330 return -1;
00331 }
00332
00333
00334
00335
00336
00337
00338 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
00339 {
00340 struct rtnl_tbf *tbf;
00341
00342 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00343 BUG();
00344
00345 if (tbf->qt_mask & TBF_ATTR_RATE)
00346 return (1 << tbf->qt_rate.rs_cell_log);
00347 else
00348 return -1;
00349 }
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
00360 int cell)
00361 {
00362 struct rtnl_tbf *tbf;
00363 int cell_log;
00364
00365 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00366 BUG();
00367
00368 cell_log = calc_cell_log(cell, bucket);
00369 if (cell_log < 0)
00370 return cell_log;
00371
00372 tbf->qt_peakrate.rs_rate = rate;
00373 tbf->qt_peakrate_bucket = bucket;
00374 tbf->qt_peakrate.rs_cell_log = cell_log;
00375 tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate);
00376
00377 tbf->qt_mask |= TBF_ATTR_PEAKRATE;
00378
00379 return 0;
00380 }
00381
00382
00383
00384
00385
00386
00387 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
00388 {
00389 struct rtnl_tbf *tbf;
00390
00391 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00392 BUG();
00393
00394 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
00395 return tbf->qt_peakrate.rs_rate;
00396 else
00397 return -1;
00398 }
00399
00400
00401
00402
00403
00404
00405 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
00406 {
00407 struct rtnl_tbf *tbf;
00408
00409 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00410 BUG();
00411
00412 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
00413 return tbf->qt_peakrate_bucket;
00414 else
00415 return -1;
00416 }
00417
00418
00419
00420
00421
00422
00423 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
00424 {
00425 struct rtnl_tbf *tbf;
00426
00427 if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
00428 BUG();
00429
00430 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
00431 return (1 << tbf->qt_peakrate.rs_cell_log);
00432 else
00433 return -1;
00434 }
00435
00436
00437
00438 static struct rtnl_tc_ops tbf_tc_ops = {
00439 .to_kind = "tbf",
00440 .to_type = RTNL_TC_TYPE_QDISC,
00441 .to_size = sizeof(struct rtnl_tbf),
00442 .to_msg_parser = tbf_msg_parser,
00443 .to_dump = {
00444 [NL_DUMP_LINE] = tbf_dump_line,
00445 [NL_DUMP_DETAILS] = tbf_dump_details,
00446 },
00447 .to_msg_fill = tbf_msg_fill,
00448 };
00449
00450 static void __init tbf_init(void)
00451 {
00452 rtnl_tc_register(&tbf_tc_ops);
00453 }
00454
00455 static void __exit tbf_exit(void)
00456 {
00457 rtnl_tc_unregister(&tbf_tc_ops);
00458 }
00459
00460