X-Git-Url: http://git.onelab.eu/?p=libnl.git;a=blobdiff_plain;f=lib%2Froute%2Fsch%2Fprio.c;fp=lib%2Froute%2Fsch%2Fprio.c;h=aa06f9cb086da9420b96506e8c730d64d9f68afc;hp=0000000000000000000000000000000000000000;hb=4cee2ecb3b8afa0637e6f5fe4c57985a4bc740ff;hpb=2df2fbe518d5a221ce6e3ee88a3fb23fb1b94b27 diff --git a/lib/route/sch/prio.c b/lib/route/sch/prio.c new file mode 100644 index 0000000..aa06f9c --- /dev/null +++ b/lib/route/sch/prio.c @@ -0,0 +1,332 @@ +/* + * lib/route/sch/prio.c PRIO Qdisc/Class + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup prio (Fast) Prio + * @brief + * + * @par 1) Typical PRIO configuration + * @code + * // Specify the maximal number of bands to be used for this PRIO qdisc. + * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS); + * + * // Provide a map assigning each priority to a band number. + * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP; + * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map)); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_PRIO_ATTR_BANDS 1 +#define SCH_PRIO_ATTR_PRIOMAP 2 +/** @endcond */ + +static inline struct rtnl_prio *prio_qdisc(struct rtnl_qdisc *qdisc) +{ + return (struct rtnl_prio *) qdisc->q_subdata; +} + +static inline struct rtnl_prio *prio_alloc(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_subdata) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_prio)); + + return prio_qdisc(qdisc); +} + +static int prio_msg_parser(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + struct tc_prio_qopt *opt; + + if (qdisc->q_opts->d_size < sizeof(*opt)) + return nl_error(EINVAL, "prio specific option size mismatch"); + + prio = prio_alloc(qdisc); + if (!prio) + return nl_errno(ENOMEM); + + opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data; + prio->qp_bands = opt->bands; + memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap)); + prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP); + + return 0; +} + +static void prio_free_data(struct rtnl_qdisc *qdisc) +{ + free(qdisc->q_subdata); +} + +static int prio_dump_brief(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_prio *prio = prio_qdisc(qdisc); + + if (prio) + dp_dump(p, " bands %u", prio->qp_bands); + + return line; +} + +static int prio_dump_full(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_prio *prio = prio_qdisc(qdisc); + int i, hp; + + if (!prio) + goto ignore; + + dp_dump(p, "priomap ["); + + for (i = 0; i <= TC_PRIO_MAX; i++) + dp_dump(p, "%u%s", prio->qp_priomap[i], + i < TC_PRIO_MAX ? " " : ""); + + dp_dump(p, "]\n"); + dp_new_line(p, line++); + + hp = (((TC_PRIO_MAX/2) + 1) & ~1); + + for (i = 0; i < hp; i++) { + char a[32]; + dp_dump(p, " %18s => %u", + rtnl_prio2str(i, a, sizeof(a)), + prio->qp_priomap[i]); + if (hp+i <= TC_PRIO_MAX) { + dp_dump(p, " %18s => %u", + rtnl_prio2str(hp+i, a, sizeof(a)), + prio->qp_priomap[hp+i]); + if (i < (hp - 1)) { + dp_dump(p, "\n"); + dp_new_line(p, line++); + } + } + } + +ignore: + return line; +} + +static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + struct tc_prio_qopt opts; + struct nl_msg *msg; + + prio = prio_qdisc(qdisc); + if (!prio || + !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)) + goto errout; + + opts.bands = prio->qp_bands; + memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap)); + + msg = nlmsg_build_no_hdr(); + if (!msg) + goto errout; + + if (nlmsg_append(msg, &opts, sizeof(opts), 0) < 0) { + nlmsg_free(msg); + goto errout; + } + + return msg; +errout: + return NULL; +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set number of bands of PRIO qdisc. + * @arg qdisc PRIO qdisc to be modified. + * @arg bands New number of bands. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands) +{ + struct rtnl_prio *prio; + + prio = prio_alloc(qdisc); + if (!prio) + return nl_errno(ENOMEM); + + prio->qp_bands = bands; + prio->qp_mask |= SCH_PRIO_ATTR_BANDS; + + return 0; +} + +/** + * Get number of bands of PRIO qdisc. + * @arg qdisc PRIO qdisc. + * @return Number of bands or a negative error code. + */ +int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + + prio = prio_qdisc(qdisc); + if (prio && prio->qp_mask & SCH_PRIO_ATTR_BANDS) + return prio->qp_bands; + else + return nl_errno(ENOMEM); +} + +/** + * Set priomap of the PRIO qdisc. + * @arg qdisc PRIO qdisc to be modified. + * @arg priomap New priority mapping. + * @arg len Length of priomap (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], + int len) +{ + struct rtnl_prio *prio; + int i; + + prio = prio_alloc(qdisc); + if (!prio) + return nl_errno(ENOMEM); + + if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS)) + return nl_error(EINVAL, "Set number of bands first"); + + if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1)) + return nl_error(ERANGE, "priomap length out of bounds"); + + for (i = 0; i <= TC_PRIO_MAX; i++) { + if (priomap[i] > prio->qp_bands) + return nl_error(ERANGE, "priomap element %d " \ + "out of bounds, increase bands number"); + } + + memcpy(prio->qp_priomap, priomap, len); + prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP; + + return 0; +} + +/** + * Get priomap of a PRIO qdisc. + * @arg qdisc PRIO qdisc. + * @return Priority mapping as array of size TC_PRIO_MAX+1 + * or NULL if an error occured. + */ +uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + + prio = prio_qdisc(qdisc); + if (prio && prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP) + return prio->qp_priomap; + else { + nl_errno(ENOENT); + return NULL; + } +} + +/** @} */ + +/** + * @name Priority Band Translations + * @{ + */ + +static struct trans_tbl prios[] = { + __ADD(TC_PRIO_BESTEFFORT,besteffort) + __ADD(TC_PRIO_FILLER,filler) + __ADD(TC_PRIO_BULK,bulk) + __ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk) + __ADD(TC_PRIO_INTERACTIVE,interactive) + __ADD(TC_PRIO_CONTROL,control) +}; + +/** + * Convert priority to character string. + * @arg prio Priority. + * @arg buf Destination buffer + * @arg size Size of destination buffer. + * + * Converts a priority to a character string and stores the result in + * the specified destination buffer. + * + * @return Name of priority as character string. + */ +char * rtnl_prio2str(int prio, char *buf, size_t size) +{ + return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios)); +} + +/** + * Convert character string to priority. + * @arg name Name of priority. + * + * Converts the provided character string specifying a priority + * to the corresponding numeric value. + * + * @return Numeric priority or a negative value if no match was found. + */ +int rtnl_str2prio(const char *name) +{ + return __str2type(name, prios, ARRAY_SIZE(prios)); +} + +/** @} */ + +static struct rtnl_qdisc_ops prio_ops = { + .qo_kind = "prio", + .qo_msg_parser = prio_msg_parser, + .qo_free_data = prio_free_data, + .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief, + .qo_dump[NL_DUMP_FULL] = prio_dump_full, + .qo_get_opts = prio_get_opts, +}; + +static struct rtnl_qdisc_ops pfifo_fast_ops = { + .qo_kind = "pfifo_fast", + .qo_msg_parser = prio_msg_parser, + .qo_free_data = prio_free_data, + .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief, + .qo_dump[NL_DUMP_FULL] = prio_dump_full, + .qo_get_opts = prio_get_opts, +}; + +static void __init prio_init(void) +{ + rtnl_qdisc_register(&prio_ops); + rtnl_qdisc_register(&pfifo_fast_ops); +} + +static void __exit prio_exit(void) +{ + rtnl_qdisc_unregister(&prio_ops); + rtnl_qdisc_unregister(&pfifo_fast_ops); +} + +/** @} */