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