Home | History | Annotate | Download | only in sch
      1 /*
      2  * lib/route/sch/prio.c		PRIO Qdisc/Class
      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 prio (Fast) Prio
     15  * @brief
     16  *
     17  * @par 1) Typical PRIO configuration
     18  * @code
     19  * // Specify the maximal number of bands to be used for this PRIO qdisc.
     20  * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS);
     21  *
     22  * // Provide a map assigning each priority to a band number.
     23  * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP;
     24  * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map));
     25  * @endcode
     26  * @{
     27  */
     28 
     29 #include <netlink-local.h>
     30 #include <netlink-tc.h>
     31 #include <netlink/netlink.h>
     32 #include <netlink/utils.h>
     33 #include <netlink/route/qdisc.h>
     34 #include <netlink/route/qdisc-modules.h>
     35 #include <netlink/route/sch/prio.h>
     36 
     37 /** @cond SKIP */
     38 #define SCH_PRIO_ATTR_BANDS	1
     39 #define SCH_PRIO_ATTR_PRIOMAP	2
     40 /** @endcond */
     41 
     42 static inline struct rtnl_prio *prio_qdisc(struct rtnl_qdisc *qdisc)
     43 {
     44 	return (struct rtnl_prio *) qdisc->q_subdata;
     45 }
     46 
     47 static inline struct rtnl_prio *prio_alloc(struct rtnl_qdisc *qdisc)
     48 {
     49 	if (!qdisc->q_subdata)
     50 		qdisc->q_subdata = calloc(1, sizeof(struct rtnl_prio));
     51 
     52 	return prio_qdisc(qdisc);
     53 }
     54 
     55 static int prio_msg_parser(struct rtnl_qdisc *qdisc)
     56 {
     57 	struct rtnl_prio *prio;
     58 	struct tc_prio_qopt *opt;
     59 
     60 	if (qdisc->q_opts->d_size < sizeof(*opt))
     61 		return -NLE_INVAL;
     62 
     63 	prio = prio_alloc(qdisc);
     64 	if (!prio)
     65 		return -NLE_NOMEM;
     66 
     67 	opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data;
     68 	prio->qp_bands = opt->bands;
     69 	memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap));
     70 	prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP);
     71 
     72 	return 0;
     73 }
     74 
     75 static void prio_free_data(struct rtnl_qdisc *qdisc)
     76 {
     77 	free(qdisc->q_subdata);
     78 }
     79 
     80 static void prio_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
     81 {
     82 	struct rtnl_prio *prio = prio_qdisc(qdisc);
     83 
     84 	if (prio)
     85 		nl_dump(p, " bands %u", prio->qp_bands);
     86 }
     87 
     88 static void prio_dump_details(struct rtnl_qdisc *qdisc,struct nl_dump_params *p)
     89 {
     90 	struct rtnl_prio *prio = prio_qdisc(qdisc);
     91 	int i, hp;
     92 
     93 	if (!prio)
     94 		return;
     95 
     96 	nl_dump(p, "priomap [");
     97 
     98 	for (i = 0; i <= TC_PRIO_MAX; i++)
     99 		nl_dump(p, "%u%s", prio->qp_priomap[i],
    100 			i < TC_PRIO_MAX ? " " : "");
    101 
    102 	nl_dump(p, "]\n");
    103 	nl_new_line(p);
    104 
    105 	hp = (((TC_PRIO_MAX/2) + 1) & ~1);
    106 
    107 	for (i = 0; i < hp; i++) {
    108 		char a[32];
    109 		nl_dump(p, "    %18s => %u",
    110 			rtnl_prio2str(i, a, sizeof(a)),
    111 			prio->qp_priomap[i]);
    112 		if (hp+i <= TC_PRIO_MAX) {
    113 			nl_dump(p, " %18s => %u",
    114 				rtnl_prio2str(hp+i, a, sizeof(a)),
    115 				prio->qp_priomap[hp+i]);
    116 			if (i < (hp - 1)) {
    117 				nl_dump(p, "\n");
    118 				nl_new_line(p);
    119 			}
    120 		}
    121 	}
    122 }
    123 
    124 static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc)
    125 {
    126 	struct rtnl_prio *prio;
    127 	struct tc_prio_qopt opts;
    128 	struct nl_msg *msg;
    129 
    130 	prio = prio_qdisc(qdisc);
    131 	if (!prio ||
    132 	    !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP))
    133 		goto errout;
    134 
    135 	opts.bands = prio->qp_bands;
    136 	memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap));
    137 
    138 	msg = nlmsg_alloc();
    139 	if (!msg)
    140 		goto errout;
    141 
    142 	if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) {
    143 		nlmsg_free(msg);
    144 		goto errout;
    145 	}
    146 
    147 	return msg;
    148 errout:
    149 	return NULL;
    150 }
    151 
    152 /**
    153  * @name Attribute Modification
    154  * @{
    155  */
    156 
    157 /**
    158  * Set number of bands of PRIO qdisc.
    159  * @arg qdisc		PRIO qdisc to be modified.
    160  * @arg bands		New number of bands.
    161  * @return 0 on success or a negative error code.
    162  */
    163 int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands)
    164 {
    165 	struct rtnl_prio *prio;
    166 
    167 	prio = prio_alloc(qdisc);
    168 	if (!prio)
    169 		return -NLE_NOMEM;
    170 
    171 	prio->qp_bands = bands;
    172 	prio->qp_mask |= SCH_PRIO_ATTR_BANDS;
    173 
    174 	return 0;
    175 }
    176 
    177 /**
    178  * Get number of bands of PRIO qdisc.
    179  * @arg qdisc		PRIO qdisc.
    180  * @return Number of bands or a negative error code.
    181  */
    182 int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc)
    183 {
    184 	struct rtnl_prio *prio;
    185 
    186 	prio = prio_qdisc(qdisc);
    187 	if (prio && prio->qp_mask & SCH_PRIO_ATTR_BANDS)
    188 		return prio->qp_bands;
    189 	else
    190 		return -NLE_NOMEM;
    191 }
    192 
    193 /**
    194  * Set priomap of the PRIO qdisc.
    195  * @arg qdisc		PRIO qdisc to be modified.
    196  * @arg priomap		New priority mapping.
    197  * @arg len		Length of priomap (# of elements).
    198  * @return 0 on success or a negative error code.
    199  */
    200 int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
    201 				int len)
    202 {
    203 	struct rtnl_prio *prio;
    204 	int i;
    205 
    206 	prio = prio_alloc(qdisc);
    207 	if (!prio)
    208 		return -NLE_NOMEM;
    209 
    210 	if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS))
    211 		return -NLE_MISSING_ATTR;
    212 
    213 	if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1))
    214 		return -NLE_RANGE;
    215 
    216 	for (i = 0; i <= TC_PRIO_MAX; i++) {
    217 		if (priomap[i] > prio->qp_bands)
    218 			return -NLE_RANGE;
    219 	}
    220 
    221 	memcpy(prio->qp_priomap, priomap, len);
    222 	prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP;
    223 
    224 	return 0;
    225 }
    226 
    227 /**
    228  * Get priomap of a PRIO qdisc.
    229  * @arg qdisc		PRIO qdisc.
    230  * @return Priority mapping as array of size TC_PRIO_MAX+1
    231  *         or NULL if an error occured.
    232  */
    233 uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc)
    234 {
    235 	struct rtnl_prio *prio;
    236 
    237 	prio = prio_qdisc(qdisc);
    238 	if (prio && prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)
    239 		return prio->qp_priomap;
    240 	else
    241 		return NULL;
    242 }
    243 
    244 /** @} */
    245 
    246 /**
    247  * @name Priority Band Translations
    248  * @{
    249  */
    250 
    251 static struct trans_tbl prios[] = {
    252 	__ADD(TC_PRIO_BESTEFFORT,besteffort)
    253 	__ADD(TC_PRIO_FILLER,filler)
    254 	__ADD(TC_PRIO_BULK,bulk)
    255 	__ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk)
    256 	__ADD(TC_PRIO_INTERACTIVE,interactive)
    257 	__ADD(TC_PRIO_CONTROL,control)
    258 };
    259 
    260 /**
    261  * Convert priority to character string.
    262  * @arg prio		Priority.
    263  * @arg buf		Destination buffer
    264  * @arg size		Size of destination buffer.
    265  *
    266  * Converts a priority to a character string and stores the result in
    267  * the specified destination buffer.
    268  *
    269  * @return Name of priority as character string.
    270  */
    271 char * rtnl_prio2str(int prio, char *buf, size_t size)
    272 {
    273 	return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios));
    274 }
    275 
    276 /**
    277  * Convert character string to priority.
    278  * @arg name		Name of priority.
    279  *
    280  * Converts the provided character string specifying a priority
    281  * to the corresponding numeric value.
    282  *
    283  * @return Numeric priority or a negative value if no match was found.
    284  */
    285 int rtnl_str2prio(const char *name)
    286 {
    287 	return __str2type(name, prios, ARRAY_SIZE(prios));
    288 }
    289 
    290 /** @} */
    291 
    292 static struct rtnl_qdisc_ops prio_ops = {
    293 	.qo_kind		= "prio",
    294 	.qo_msg_parser		= prio_msg_parser,
    295 	.qo_free_data		= prio_free_data,
    296 	.qo_dump = {
    297 	    [NL_DUMP_LINE]	= prio_dump_line,
    298 	    [NL_DUMP_DETAILS]	= prio_dump_details,
    299 	},
    300 	.qo_get_opts		= prio_get_opts,
    301 };
    302 
    303 static struct rtnl_qdisc_ops pfifo_fast_ops = {
    304 	.qo_kind		= "pfifo_fast",
    305 	.qo_msg_parser		= prio_msg_parser,
    306 	.qo_free_data		= prio_free_data,
    307 	.qo_dump = {
    308 	    [NL_DUMP_LINE]	= prio_dump_line,
    309 	    [NL_DUMP_DETAILS]	= prio_dump_details,
    310 	},
    311 	.qo_get_opts		= prio_get_opts,
    312 };
    313 
    314 static void __init prio_init(void)
    315 {
    316 	rtnl_qdisc_register(&prio_ops);
    317 	rtnl_qdisc_register(&pfifo_fast_ops);
    318 }
    319 
    320 static void __exit prio_exit(void)
    321 {
    322 	rtnl_qdisc_unregister(&prio_ops);
    323 	rtnl_qdisc_unregister(&pfifo_fast_ops);
    324 }
    325 
    326 /** @} */
    327