libnl 3.0
|
00001 /* 00002 * lib/route/tc.c Traffic Control 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup rtnl 00014 * @defgroup tc Traffic Control 00015 * @{ 00016 */ 00017 00018 #include <netlink-local.h> 00019 #include <netlink-tc.h> 00020 #include <netlink/netlink.h> 00021 #include <netlink/utils.h> 00022 #include <netlink/route/rtnl.h> 00023 #include <netlink/route/link.h> 00024 #include <netlink/route/tc.h> 00025 #include <netlink/route/tc-api.h> 00026 00027 /** @cond SKIP */ 00028 00029 static struct nl_list_head tc_ops_list[__RTNL_TC_TYPE_MAX]; 00030 static struct rtnl_tc_type_ops *tc_type_ops[__RTNL_TC_TYPE_MAX]; 00031 00032 static struct nla_policy tc_policy[TCA_MAX+1] = { 00033 [TCA_KIND] = { .type = NLA_STRING, 00034 .maxlen = TCKINDSIZ }, 00035 [TCA_STATS] = { .minlen = sizeof(struct tc_stats) }, 00036 [TCA_STATS2] = { .type = NLA_NESTED }, 00037 }; 00038 00039 int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tc *g, 00040 struct nla_policy *policy) 00041 { 00042 00043 if (g->ce_mask & TCA_ATTR_OPTS) 00044 return nla_parse(tb, maxattr, 00045 (struct nlattr *) g->tc_opts->d_data, 00046 g->tc_opts->d_size, policy); 00047 else { 00048 /* Ugly but tb[] must be in a defined state even if no 00049 * attributes can be found. */ 00050 memset(tb, 0, sizeof(struct nlattr *) * (maxattr + 1)); 00051 return 0; 00052 } 00053 } 00054 00055 static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = { 00056 [TCA_STATS_BASIC] = { .minlen = sizeof(struct gnet_stats_basic) }, 00057 [TCA_STATS_RATE_EST] = { .minlen = sizeof(struct gnet_stats_rate_est) }, 00058 [TCA_STATS_QUEUE] = { .minlen = sizeof(struct gnet_stats_queue) }, 00059 }; 00060 00061 int rtnl_tc_msg_parse(struct nlmsghdr *n, struct rtnl_tc *tc) 00062 { 00063 struct rtnl_tc_ops *ops; 00064 struct nlattr *tb[TCA_MAX + 1]; 00065 char kind[TCKINDSIZ]; 00066 struct tcmsg *tm; 00067 int err; 00068 00069 tc->ce_msgtype = n->nlmsg_type; 00070 00071 err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy); 00072 if (err < 0) 00073 return err; 00074 00075 if (tb[TCA_KIND] == NULL) 00076 return -NLE_MISSING_ATTR; 00077 00078 nla_strlcpy(kind, tb[TCA_KIND], sizeof(kind)); 00079 rtnl_tc_set_kind(tc, kind); 00080 00081 tm = nlmsg_data(n); 00082 tc->tc_family = tm->tcm_family; 00083 tc->tc_ifindex = tm->tcm_ifindex; 00084 tc->tc_handle = tm->tcm_handle; 00085 tc->tc_parent = tm->tcm_parent; 00086 tc->tc_info = tm->tcm_info; 00087 00088 tc->ce_mask |= (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE| 00089 TCA_ATTR_PARENT | TCA_ATTR_INFO); 00090 00091 if (tb[TCA_OPTIONS]) { 00092 tc->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]); 00093 if (!tc->tc_opts) 00094 return -NLE_NOMEM; 00095 tc->ce_mask |= TCA_ATTR_OPTS; 00096 } 00097 00098 if (tb[TCA_STATS2]) { 00099 struct nlattr *tbs[TCA_STATS_MAX + 1]; 00100 00101 err = nla_parse_nested(tbs, TCA_STATS_MAX, tb[TCA_STATS2], 00102 tc_stats2_policy); 00103 if (err < 0) 00104 return err; 00105 00106 if (tbs[TCA_STATS_BASIC]) { 00107 struct gnet_stats_basic *bs; 00108 00109 bs = nla_data(tbs[TCA_STATS_BASIC]); 00110 tc->tc_stats[RTNL_TC_BYTES] = bs->bytes; 00111 tc->tc_stats[RTNL_TC_PACKETS] = bs->packets; 00112 } 00113 00114 if (tbs[TCA_STATS_RATE_EST]) { 00115 struct gnet_stats_rate_est *re; 00116 00117 re = nla_data(tbs[TCA_STATS_RATE_EST]); 00118 tc->tc_stats[RTNL_TC_RATE_BPS] = re->bps; 00119 tc->tc_stats[RTNL_TC_RATE_PPS] = re->pps; 00120 } 00121 00122 if (tbs[TCA_STATS_QUEUE]) { 00123 struct gnet_stats_queue *q; 00124 00125 q = nla_data(tbs[TCA_STATS_QUEUE]); 00126 tc->tc_stats[RTNL_TC_QLEN] = q->qlen; 00127 tc->tc_stats[RTNL_TC_BACKLOG] = q->backlog; 00128 tc->tc_stats[RTNL_TC_DROPS] = q->drops; 00129 tc->tc_stats[RTNL_TC_REQUEUES] = q->requeues; 00130 tc->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits; 00131 } 00132 00133 tc->ce_mask |= TCA_ATTR_STATS; 00134 00135 if (tbs[TCA_STATS_APP]) { 00136 tc->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]); 00137 if (tc->tc_xstats == NULL) 00138 return -NLE_NOMEM; 00139 } else 00140 goto compat_xstats; 00141 } else { 00142 if (tb[TCA_STATS]) { 00143 struct tc_stats *st = nla_data(tb[TCA_STATS]); 00144 00145 tc->tc_stats[RTNL_TC_BYTES] = st->bytes; 00146 tc->tc_stats[RTNL_TC_PACKETS] = st->packets; 00147 tc->tc_stats[RTNL_TC_RATE_BPS] = st->bps; 00148 tc->tc_stats[RTNL_TC_RATE_PPS] = st->pps; 00149 tc->tc_stats[RTNL_TC_QLEN] = st->qlen; 00150 tc->tc_stats[RTNL_TC_BACKLOG] = st->backlog; 00151 tc->tc_stats[RTNL_TC_DROPS] = st->drops; 00152 tc->tc_stats[RTNL_TC_OVERLIMITS]= st->overlimits; 00153 00154 tc->ce_mask |= TCA_ATTR_STATS; 00155 } 00156 00157 compat_xstats: 00158 if (tb[TCA_XSTATS]) { 00159 tc->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]); 00160 if (tc->tc_xstats == NULL) 00161 return -NLE_NOMEM; 00162 tc->ce_mask |= TCA_ATTR_XSTATS; 00163 } 00164 } 00165 00166 ops = rtnl_tc_get_ops(tc); 00167 if (ops && ops->to_msg_parser) { 00168 void *data = rtnl_tc_data(tc); 00169 00170 if (!data) 00171 return -NLE_NOMEM; 00172 00173 err = ops->to_msg_parser(tc, data); 00174 if (err < 0) 00175 return err; 00176 } 00177 00178 return 0; 00179 } 00180 00181 int rtnl_tc_msg_build(struct rtnl_tc *tc, int type, int flags, 00182 struct nl_msg **result) 00183 { 00184 struct nl_msg *msg; 00185 struct rtnl_tc_ops *ops; 00186 struct tcmsg tchdr = { 00187 .tcm_family = AF_UNSPEC, 00188 .tcm_ifindex = tc->tc_ifindex, 00189 .tcm_handle = tc->tc_handle, 00190 .tcm_parent = tc->tc_parent, 00191 }; 00192 int err = -NLE_MSGSIZE; 00193 00194 msg = nlmsg_alloc_simple(type, flags); 00195 if (!msg) 00196 return -NLE_NOMEM; 00197 00198 if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) 00199 goto nla_put_failure; 00200 00201 if (tc->ce_mask & TCA_ATTR_KIND) 00202 NLA_PUT_STRING(msg, TCA_KIND, tc->tc_kind); 00203 00204 ops = rtnl_tc_get_ops(tc); 00205 if (ops && ops->to_msg_fill) { 00206 struct nlattr *opts; 00207 void *data = rtnl_tc_data(tc); 00208 00209 if (!(opts = nla_nest_start(msg, TCA_OPTIONS))) 00210 goto nla_put_failure; 00211 00212 if ((err = ops->to_msg_fill(tc, data, msg)) < 0) 00213 goto nla_put_failure; 00214 00215 nla_nest_end(msg, opts); 00216 } 00217 00218 *result = msg; 00219 return 0; 00220 00221 nla_put_failure: 00222 nlmsg_free(msg); 00223 return err; 00224 } 00225 00226 void tca_set_kind(struct rtnl_tc *t, const char *kind) 00227 { 00228 strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1); 00229 t->ce_mask |= TCA_ATTR_KIND; 00230 } 00231 00232 00233 /** @endcond */ 00234 00235 /** 00236 * @name Attributes 00237 * @{ 00238 */ 00239 00240 /** 00241 * Set interface index of traffic control object 00242 * @arg tc traffic control object 00243 * @arg ifindex interface index. 00244 * 00245 * Sets the interface index of a traffic control object. The interface 00246 * index defines the network device which this tc object is attached to. 00247 * This function will overwrite any network device assigned with previous 00248 * calls to rtnl_tc_set_ifindex() or rtnl_tc_set_link(). 00249 */ 00250 void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex) 00251 { 00252 /* Obsolete possible old link reference */ 00253 rtnl_link_put(tc->tc_link); 00254 tc->tc_link = NULL; 00255 tc->ce_mask &= ~TCA_ATTR_LINK; 00256 00257 tc->tc_ifindex = ifindex; 00258 tc->ce_mask |= TCA_ATTR_IFINDEX; 00259 } 00260 00261 /** 00262 * Return interface index of traffic control object 00263 * @arg tc traffic control object 00264 */ 00265 int rtnl_tc_get_ifindex(struct rtnl_tc *tc) 00266 { 00267 return tc->tc_ifindex; 00268 } 00269 00270 /** 00271 * Set link of traffic control object 00272 * @arg tc traffic control object 00273 * @arg link link object 00274 * 00275 * Sets the link of a traffic control object. This function serves 00276 * the same purpose as rtnl_tc_set_ifindex() but due to the continued 00277 * allowed access to the link object it gives it the possibility to 00278 * retrieve sane default values for the the MTU and the linktype. 00279 * Always prefer this function over rtnl_tc_set_ifindex() if you can 00280 * spare to have an additional link object around. 00281 */ 00282 void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link) 00283 { 00284 rtnl_link_put(tc->tc_link); 00285 00286 if (!link) 00287 return; 00288 00289 nl_object_get(OBJ_CAST(link)); 00290 tc->tc_link = link; 00291 tc->tc_ifindex = link->l_index; 00292 tc->ce_mask |= TCA_ATTR_LINK | TCA_ATTR_IFINDEX; 00293 } 00294 00295 /** 00296 * Set the Maximum Transmission Unit (MTU) of traffic control object 00297 * @arg tc traffic control object 00298 * @arg mtu largest packet size expected 00299 * 00300 * Sets the MTU of a traffic control object. Not all traffic control 00301 * objects will make use of this but it helps while calculating rate 00302 * tables. This value is typically derived directly from the link 00303 * the tc object is attached to if the link has been assigned via 00304 * rtnl_tc_set_link(). It is usually not necessary to set the MTU 00305 * manually, this function is provided to allow overwriting the derived 00306 * value. 00307 */ 00308 void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu) 00309 { 00310 tc->tc_mtu = mtu; 00311 tc->ce_mask |= TCA_ATTR_MTU; 00312 } 00313 00314 /** 00315 * Return the MTU of traffic control object 00316 * @arg tc traffic control object 00317 * 00318 * Returns the MTU of a traffic control object which has been set via: 00319 * -# User specified value set via rtnl_tc_set_mtu() 00320 * -# Dervied from link set via rtnl_tc_set_link() 00321 * -# Fall back to default: ethernet = 1600 00322 */ 00323 uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc) 00324 { 00325 if (tc->ce_mask & TCA_ATTR_MTU) 00326 return tc->tc_mtu; 00327 else if (tc->ce_mask & TCA_ATTR_LINK) 00328 return tc->tc_link->l_mtu; 00329 else 00330 return 1600; /* default to ethernet */ 00331 } 00332 00333 /** 00334 * Set the Minimum Packet Unit (MPU) of a traffic control object 00335 * @arg tc traffic control object 00336 * @arg mpu minimum packet size expected 00337 * 00338 * Sets the MPU of a traffic contorl object. It specifies the minimum 00339 * packet size to ever hit this traffic control object. Not all traffic 00340 * control objects will make use of this but it helps while calculating 00341 * rate tables. 00342 */ 00343 void rtnl_tc_set_mpu(struct rtnl_tc *tc, uint32_t mpu) 00344 { 00345 tc->tc_mpu = mpu; 00346 tc->ce_mask |= TCA_ATTR_MPU; 00347 } 00348 00349 /** 00350 * Return the Minimum Packet Unit (MPU) of a traffic control object 00351 * @arg tc traffic control object 00352 * 00353 * @return The MPU previously set via rtnl_tc_set_mpu() or 0. 00354 */ 00355 uint32_t rtnl_tc_get_mpu(struct rtnl_tc *tc) 00356 { 00357 return tc->tc_mpu; 00358 } 00359 00360 /** 00361 * Set per packet overhead of a traffic control object 00362 * @arg tc traffic control object 00363 * @arg overhead overhead per packet in bytes 00364 * 00365 * Sets the per packet overhead in bytes occuring on the link not seen 00366 * by the kernel. This value can be used to correct size calculations 00367 * if the packet size on the wire does not match the packet sizes seen 00368 * in the network stack. Not all traffic control objects will make use 00369 * this but it helps while calculating accurate packet sizes in the 00370 * kernel. 00371 */ 00372 void rtnl_tc_set_overhead(struct rtnl_tc *tc, uint32_t overhead) 00373 { 00374 tc->tc_overhead = overhead; 00375 tc->ce_mask |= TCA_ATTR_OVERHEAD; 00376 } 00377 00378 /** 00379 * Return per packet overhead of a traffic control object 00380 * @arg tc traffic control object 00381 * 00382 * @return The overhead previously set by rtnl_tc_set_overhead() or 0. 00383 */ 00384 uint32_t rtnl_tc_get_overhead(struct rtnl_tc *tc) 00385 { 00386 return tc->tc_overhead; 00387 } 00388 00389 /** 00390 * Set the linktype of a traffic control object 00391 * @arg tc traffic control object 00392 * @arg type type of link (e.g. ARPHRD_ATM, ARPHRD_ETHER) 00393 * 00394 * Overwrites the type of link this traffic control object is attached to. 00395 * This value is typically derived from the link this tc object is attached 00396 * if the link has been assigned via rtnl_tc_set_link(). It is usually not 00397 * necessary to set the linktype manually. This function is provided to 00398 * allow overwriting the linktype. 00399 */ 00400 void rtnl_tc_set_linktype(struct rtnl_tc *tc, uint32_t type) 00401 { 00402 tc->tc_linktype = type; 00403 tc->ce_mask |= TCA_ATTR_LINKTYPE; 00404 } 00405 00406 /** 00407 * Return the linktype of a traffic control object 00408 * @arg tc traffic control object 00409 * 00410 * Returns the linktype of the link the traffic control object is attached to: 00411 * -# User specified value via rtnl_tc_set_linktype() 00412 * -# Value derived from link set via rtnl_tc_set_link() 00413 * -# Default fall-back: ARPHRD_ETHER 00414 */ 00415 uint32_t rtnl_tc_get_linktype(struct rtnl_tc *tc) 00416 { 00417 if (tc->ce_mask & TCA_ATTR_LINKTYPE) 00418 return tc->tc_linktype; 00419 else if (tc->ce_mask & TCA_ATTR_LINK) 00420 return tc->tc_link->l_arptype; 00421 else 00422 return ARPHRD_ETHER; /* default to ethernet */ 00423 } 00424 00425 /** 00426 * Set identifier of traffic control object 00427 * @arg tc traffic control object 00428 * @arg id unique identifier 00429 */ 00430 void rtnl_tc_set_handle(struct rtnl_tc *tc, uint32_t id) 00431 { 00432 tc->tc_handle = id; 00433 tc->ce_mask |= TCA_ATTR_HANDLE; 00434 } 00435 00436 /** 00437 * Return identifier of a traffic control object 00438 * @arg tc traffic control object 00439 */ 00440 uint32_t rtnl_tc_get_handle(struct rtnl_tc *tc) 00441 { 00442 return tc->tc_handle; 00443 } 00444 00445 /** 00446 * Set the parent identifier of a traffic control object 00447 * @arg tc traffic control object 00448 * @arg parent identifier of parent traffif control object 00449 * 00450 */ 00451 void rtnl_tc_set_parent(struct rtnl_tc *tc, uint32_t parent) 00452 { 00453 tc->tc_parent = parent; 00454 tc->ce_mask |= TCA_ATTR_PARENT; 00455 } 00456 00457 /** 00458 * Return parent identifier of a traffic control object 00459 * @arg tc traffic control object 00460 */ 00461 uint32_t rtnl_tc_get_parent(struct rtnl_tc *tc) 00462 { 00463 return tc->tc_parent; 00464 } 00465 00466 /** 00467 * Define the type of traffic control object 00468 * @arg tc traffic control object 00469 * @arg kind name of the tc object type 00470 * 00471 * @return 0 on success or a negative error code 00472 */ 00473 int rtnl_tc_set_kind(struct rtnl_tc *tc, const char *kind) 00474 { 00475 if (tc->ce_mask & TCA_ATTR_KIND) 00476 return -NLE_EXIST; 00477 00478 strncpy(tc->tc_kind, kind, sizeof(tc->tc_kind) - 1); 00479 tc->ce_mask |= TCA_ATTR_KIND; 00480 00481 /* Force allocation of data */ 00482 rtnl_tc_data(tc); 00483 00484 return 0; 00485 } 00486 00487 /** 00488 * Return kind of traffic control object 00489 * @arg tc traffic control object 00490 * 00491 * @return Kind of traffic control object or NULL if not set. 00492 */ 00493 char *rtnl_tc_get_kind(struct rtnl_tc *tc) 00494 { 00495 if (tc->ce_mask & TCA_ATTR_KIND) 00496 return tc->tc_kind; 00497 else 00498 return NULL; 00499 } 00500 00501 /** 00502 * Return value of a statistical counter of a traffic control object 00503 * @arg tc traffic control object 00504 * @arg id identifier of statistical counter 00505 * 00506 * @return Value of requested statistic counter or 0. 00507 */ 00508 uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id) 00509 { 00510 if (id < 0 || id > RTNL_TC_STATS_MAX) 00511 return 0; 00512 00513 return tc->tc_stats[id]; 00514 } 00515 00516 /** @} */ 00517 00518 /** 00519 * @name Utilities 00520 * @{ 00521 */ 00522 00523 /** 00524 * Calculate time required to transmit buffer at a specific rate 00525 * @arg bufsize Size of buffer to be transmited in bytes. 00526 * @arg rate Transmit rate in bytes per second. 00527 * 00528 * Calculates the number of micro seconds required to transmit a 00529 * specific buffer at a specific transmit rate. 00530 * 00531 * @f[ 00532 * txtime=\frac{bufsize}{rate}10^6 00533 * @f] 00534 * 00535 * @return Required transmit time in micro seconds. 00536 */ 00537 int rtnl_tc_calc_txtime(int bufsize, int rate) 00538 { 00539 double tx_time_secs; 00540 00541 tx_time_secs = (double) bufsize / (double) rate; 00542 00543 return tx_time_secs * 1000000.; 00544 } 00545 00546 /** 00547 * Calculate buffer size able to transmit in a specific time and rate. 00548 * @arg txtime Available transmit time in micro seconds. 00549 * @arg rate Transmit rate in bytes per second. 00550 * 00551 * Calculates the size of the buffer that can be transmitted in a 00552 * specific time period at a specific transmit rate. 00553 * 00554 * @f[ 00555 * bufsize=\frac{{txtime} \times {rate}}{10^6} 00556 * @f] 00557 * 00558 * @return Size of buffer in bytes. 00559 */ 00560 int rtnl_tc_calc_bufsize(int txtime, int rate) 00561 { 00562 double bufsize; 00563 00564 bufsize = (double) txtime * (double) rate; 00565 00566 return bufsize / 1000000.; 00567 } 00568 00569 /** 00570 * Calculate the binary logarithm for a specific cell size 00571 * @arg cell_size Size of cell, must be a power of two. 00572 * @return Binary logirhtm of cell size or a negative error code. 00573 */ 00574 int rtnl_tc_calc_cell_log(int cell_size) 00575 { 00576 int i; 00577 00578 for (i = 0; i < 32; i++) 00579 if ((1 << i) == cell_size) 00580 return i; 00581 00582 return -NLE_INVAL; 00583 } 00584 00585 00586 /** @} */ 00587 00588 /** 00589 * @name Rate Tables 00590 * @{ 00591 */ 00592 00593 /* 00594 * COPYRIGHT NOTE: 00595 * align_to_atm() and adjust_size() derived/coped from iproute2 source. 00596 */ 00597 00598 /* 00599 * The align to ATM cells is used for determining the (ATM) SAR 00600 * alignment overhead at the ATM layer. (SAR = Segmentation And 00601 * Reassembly). This is for example needed when scheduling packet on 00602 * an ADSL connection. Note that the extra ATM-AAL overhead is _not_ 00603 * included in this calculation. This overhead is added in the kernel 00604 * before doing the rate table lookup, as this gives better precision 00605 * (as the table will always be aligned for 48 bytes). 00606 * --Hawk, d.7/11-2004. <hawk@diku.dk> 00607 */ 00608 static unsigned int align_to_atm(unsigned int size) 00609 { 00610 int linksize, cells; 00611 cells = size / ATM_CELL_PAYLOAD; 00612 if ((size % ATM_CELL_PAYLOAD) > 0) 00613 cells++; 00614 00615 linksize = cells * ATM_CELL_SIZE; /* Use full cell size to add ATM tax */ 00616 return linksize; 00617 } 00618 00619 static unsigned int adjust_size(unsigned int size, unsigned int mpu, 00620 uint32_t linktype) 00621 { 00622 if (size < mpu) 00623 size = mpu; 00624 00625 switch (linktype) { 00626 case ARPHRD_ATM: 00627 return align_to_atm(size); 00628 00629 case ARPHRD_ETHER: 00630 default: 00631 return size; 00632 } 00633 } 00634 00635 /** 00636 * Compute a transmission time lookup table 00637 * @arg tc traffic control object 00638 * @arg spec Rate specification 00639 * @arg dst Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[]. 00640 * 00641 * Computes a table of RTNL_TC_RTABLE_SIZE entries specyfing the 00642 * transmission times for various packet sizes, e.g. the transmission 00643 * time for a packet of size \c pktsize could be looked up: 00644 * @code 00645 * txtime = table[pktsize >> log2(mtu)]; 00646 * @endcode 00647 */ 00648 int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *spec, 00649 uint32_t *dst) 00650 { 00651 uint32_t mtu = rtnl_tc_get_mtu(tc); 00652 uint32_t linktype = rtnl_tc_get_linktype(tc); 00653 uint8_t cell_log = spec->rs_cell_log; 00654 unsigned int size, i; 00655 00656 spec->rs_mpu = rtnl_tc_get_mpu(tc); 00657 spec->rs_overhead = rtnl_tc_get_overhead(tc); 00658 00659 if (mtu == 0) 00660 mtu = 2047; 00661 00662 if (cell_log == UINT8_MAX) { 00663 /* 00664 * cell_log not specified, calculate it. It has to specify the 00665 * minimum number of rshifts required to break the MTU to below 00666 * RTNL_TC_RTABLE_SIZE. 00667 */ 00668 cell_log = 0; 00669 while ((mtu >> cell_log) >= RTNL_TC_RTABLE_SIZE) 00670 cell_log++; 00671 } 00672 00673 for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) { 00674 size = adjust_size((i + 1) << cell_log, spec->rs_mpu, linktype); 00675 dst[i] = rtnl_tc_calc_txtime(size, spec->rs_rate); 00676 } 00677 00678 spec->rs_cell_align = -1; 00679 spec->rs_cell_log = cell_log; 00680 00681 return 0; 00682 } 00683 00684 /** @} */ 00685 00686 /** 00687 * @name TC implementation of cache functions 00688 */ 00689 00690 void rtnl_tc_free_data(struct nl_object *obj) 00691 { 00692 struct rtnl_tc *tc = TC_CAST(obj); 00693 struct rtnl_tc_ops *ops; 00694 00695 rtnl_link_put(tc->tc_link); 00696 nl_data_free(tc->tc_opts); 00697 nl_data_free(tc->tc_xstats); 00698 00699 if (tc->tc_subdata) { 00700 ops = rtnl_tc_get_ops(tc); 00701 if (ops && ops->to_free_data) 00702 ops->to_free_data(tc, nl_data_get(tc->tc_subdata)); 00703 00704 nl_data_free(tc->tc_subdata); 00705 } 00706 } 00707 00708 int rtnl_tc_clone(struct nl_object *dstobj, struct nl_object *srcobj) 00709 { 00710 struct rtnl_tc *dst = TC_CAST(dstobj); 00711 struct rtnl_tc *src = TC_CAST(srcobj); 00712 struct rtnl_tc_ops *ops; 00713 00714 if (src->tc_link) { 00715 dst->tc_link = (struct rtnl_link *) 00716 nl_object_clone(OBJ_CAST(src->tc_link)); 00717 if (!dst->tc_link) 00718 return -NLE_NOMEM; 00719 } 00720 00721 if (src->tc_opts) { 00722 dst->tc_opts = nl_data_clone(src->tc_opts); 00723 if (!dst->tc_opts) 00724 return -NLE_NOMEM; 00725 } 00726 00727 if (src->tc_xstats) { 00728 dst->tc_xstats = nl_data_clone(src->tc_xstats); 00729 if (!dst->tc_xstats) 00730 return -NLE_NOMEM; 00731 } 00732 00733 if (src->tc_subdata) { 00734 if (!(dst->tc_subdata = nl_data_clone(src->tc_subdata))) { 00735 return -NLE_NOMEM; 00736 } 00737 } 00738 00739 ops = rtnl_tc_get_ops(src); 00740 if (ops && ops->to_clone) { 00741 void *a = rtnl_tc_data(dst), *b = rtnl_tc_data(src); 00742 00743 if (!a) 00744 return 0; 00745 else if (!b) 00746 return -NLE_NOMEM; 00747 00748 return ops->to_clone(a, b); 00749 } 00750 00751 return 0; 00752 } 00753 00754 static int tc_dump(struct rtnl_tc *tc, enum nl_dump_type type, 00755 struct nl_dump_params *p) 00756 { 00757 struct rtnl_tc_type_ops *type_ops; 00758 struct rtnl_tc_ops *ops; 00759 void *data = rtnl_tc_data(tc); 00760 00761 type_ops = tc_type_ops[tc->tc_type]; 00762 if (type_ops && type_ops->tt_dump[type]) 00763 type_ops->tt_dump[type](tc, p); 00764 00765 ops = rtnl_tc_get_ops(tc); 00766 if (ops && ops->to_dump[type]) { 00767 ops->to_dump[type](tc, data, p); 00768 return 1; 00769 } 00770 00771 return 0; 00772 } 00773 00774 void rtnl_tc_dump_line(struct nl_object *obj, struct nl_dump_params *p) 00775 { 00776 struct rtnl_tc_type_ops *type_ops; 00777 struct rtnl_tc *tc = TC_CAST(obj); 00778 struct nl_cache *link_cache; 00779 char buf[32]; 00780 00781 nl_new_line(p); 00782 00783 type_ops = tc_type_ops[tc->tc_type]; 00784 if (type_ops && type_ops->tt_dump_prefix) 00785 nl_dump(p, "%s ", type_ops->tt_dump_prefix); 00786 00787 nl_dump(p, "%s ", tc->tc_kind); 00788 00789 if ((link_cache = nl_cache_mngt_require("route/link"))) { 00790 nl_dump(p, "dev %s ", 00791 rtnl_link_i2name(link_cache, tc->tc_ifindex, 00792 buf, sizeof(buf))); 00793 } else 00794 nl_dump(p, "dev %u ", tc->tc_ifindex); 00795 00796 nl_dump(p, "id %s ", 00797 rtnl_tc_handle2str(tc->tc_handle, buf, sizeof(buf))); 00798 00799 nl_dump(p, "parent %s", 00800 rtnl_tc_handle2str(tc->tc_parent, buf, sizeof(buf))); 00801 00802 tc_dump(tc, NL_DUMP_LINE, p); 00803 nl_dump(p, "\n"); 00804 } 00805 00806 void rtnl_tc_dump_details(struct nl_object *obj, struct nl_dump_params *p) 00807 { 00808 struct rtnl_tc *tc = TC_CAST(obj); 00809 00810 rtnl_tc_dump_line(OBJ_CAST(tc), p); 00811 00812 nl_dump_line(p, " "); 00813 00814 if (tc->ce_mask & TCA_ATTR_MTU) 00815 nl_dump(p, " mtu %u", tc->tc_mtu); 00816 00817 if (tc->ce_mask & TCA_ATTR_MPU) 00818 nl_dump(p, " mpu %u", tc->tc_mpu); 00819 00820 if (tc->ce_mask & TCA_ATTR_OVERHEAD) 00821 nl_dump(p, " overhead %u", tc->tc_overhead); 00822 00823 if (!tc_dump(tc, NL_DUMP_DETAILS, p)) 00824 nl_dump(p, "no options"); 00825 nl_dump(p, "\n"); 00826 } 00827 00828 void rtnl_tc_dump_stats(struct nl_object *obj, struct nl_dump_params *p) 00829 { 00830 struct rtnl_tc *tc = TC_CAST(obj); 00831 char *unit, fmt[64]; 00832 float res; 00833 00834 rtnl_tc_dump_details(OBJ_CAST(tc), p); 00835 00836 strcpy(fmt, " %7.2f %s %10u %10u %10u %10u %10u\n"); 00837 00838 nl_dump_line(p, 00839 " Stats: bytes packets drops overlimits" \ 00840 " qlen backlog\n"); 00841 00842 res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_BYTES], &unit); 00843 if (*unit == 'B') 00844 fmt[11] = '9'; 00845 00846 nl_dump_line(p, fmt, res, unit, 00847 tc->tc_stats[RTNL_TC_PACKETS], 00848 tc->tc_stats[RTNL_TC_DROPS], 00849 tc->tc_stats[RTNL_TC_OVERLIMITS], 00850 tc->tc_stats[RTNL_TC_QLEN], 00851 tc->tc_stats[RTNL_TC_BACKLOG]); 00852 00853 res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_RATE_BPS], &unit); 00854 00855 strcpy(fmt, " %7.2f %s/s%9u pps"); 00856 00857 if (*unit == 'B') 00858 fmt[11] = '9'; 00859 00860 nl_dump_line(p, fmt, res, unit, tc->tc_stats[RTNL_TC_RATE_PPS]); 00861 00862 tc_dump(tc, NL_DUMP_LINE, p); 00863 nl_dump(p, "\n"); 00864 } 00865 00866 int rtnl_tc_compare(struct nl_object *aobj, struct nl_object *bobj, 00867 uint32_t attrs, int flags) 00868 { 00869 struct rtnl_tc *a = TC_CAST(aobj); 00870 struct rtnl_tc *b = TC_CAST(bobj); 00871 int diff = 0; 00872 00873 #define TC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TCA_ATTR_##ATTR, a, b, EXPR) 00874 00875 diff |= TC_DIFF(HANDLE, a->tc_handle != b->tc_handle); 00876 diff |= TC_DIFF(PARENT, a->tc_parent != b->tc_parent); 00877 diff |= TC_DIFF(IFINDEX, a->tc_ifindex != b->tc_ifindex); 00878 diff |= TC_DIFF(KIND, strcmp(a->tc_kind, b->tc_kind)); 00879 00880 #undef TC_DIFF 00881 00882 return diff; 00883 } 00884 00885 /** @} */ 00886 00887 /** 00888 * @name Modules API 00889 */ 00890 00891 struct rtnl_tc_ops *rtnl_tc_lookup_ops(enum rtnl_tc_type type, const char *kind) 00892 { 00893 struct rtnl_tc_ops *ops; 00894 00895 nl_list_for_each_entry(ops, &tc_ops_list[type], to_list) 00896 if (!strcmp(kind, ops->to_kind)) 00897 return ops; 00898 00899 return NULL; 00900 } 00901 00902 struct rtnl_tc_ops *rtnl_tc_get_ops(struct rtnl_tc *tc) 00903 { 00904 if (!tc->tc_ops) 00905 tc->tc_ops = rtnl_tc_lookup_ops(tc->tc_type, tc->tc_kind); 00906 00907 return tc->tc_ops; 00908 } 00909 00910 /** 00911 * Register a traffic control module 00912 * @arg ops traffic control module operations 00913 */ 00914 int rtnl_tc_register(struct rtnl_tc_ops *ops) 00915 { 00916 static int init = 0; 00917 00918 /* 00919 * Initialiation hack, make sure list is initialized when 00920 * the first tc module registers. Putting this in a 00921 * separate __init would required correct ordering of init 00922 * functions 00923 */ 00924 if (!init) { 00925 int i; 00926 00927 for (i = 0; i < __RTNL_TC_TYPE_MAX; i++) 00928 nl_init_list_head(&tc_ops_list[i]); 00929 00930 init = 1; 00931 } 00932 00933 if (!ops->to_kind || ops->to_type > RTNL_TC_TYPE_MAX) 00934 BUG(); 00935 00936 if (rtnl_tc_lookup_ops(ops->to_type, ops->to_kind)) 00937 return -NLE_EXIST; 00938 00939 nl_list_add_tail(&ops->to_list, &tc_ops_list[ops->to_type]); 00940 00941 return 0; 00942 } 00943 00944 /** 00945 * Unregister a traffic control module 00946 * @arg ops traffic control module operations 00947 */ 00948 void rtnl_tc_unregister(struct rtnl_tc_ops *ops) 00949 { 00950 nl_list_del(&ops->to_list); 00951 } 00952 00953 void *rtnl_tc_data(struct rtnl_tc *tc) 00954 { 00955 if (!tc->tc_subdata) { 00956 size_t size; 00957 00958 if (!tc->tc_ops) { 00959 if (!tc->tc_kind) 00960 BUG(); 00961 00962 if (!rtnl_tc_get_ops(tc)) 00963 return NULL; 00964 } 00965 00966 if (!(size = tc->tc_ops->to_size)) 00967 BUG(); 00968 00969 if (!(tc->tc_subdata = nl_data_alloc(NULL, size))) 00970 return NULL; 00971 } 00972 00973 return nl_data_get(tc->tc_subdata); 00974 } 00975 00976 void rtnl_tc_type_register(struct rtnl_tc_type_ops *ops) 00977 { 00978 if (ops->tt_type > RTNL_TC_TYPE_MAX) 00979 BUG(); 00980 00981 tc_type_ops[ops->tt_type] = ops; 00982 } 00983 00984 void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *ops) 00985 { 00986 if (ops->tt_type > RTNL_TC_TYPE_MAX) 00987 BUG(); 00988 00989 tc_type_ops[ops->tt_type] = NULL; 00990 } 00991 00992 /** @} */ 00993 00994 /** @} */