mirror of
				https://git.proxmox.com/git/mirror_iproute2
				synced 2025-10-31 08:42:39 +00:00 
			
		
		
		
	 d2773f1261
			
		
	
	
		d2773f1261
		
	
	
	
	
		
			
			Add a new module to generate and parse options specific to the ETS Qdisc.
Example output:
    bands 8 strict 3 priomap 0 1 2 3 4 5 6 7
qdisc ets 1: root refcnt 2 offloaded bands 8 strict 3 quanta 1514 1514 1514 1514 1514 priomap 0 1 2 3 4 5 6 7 7 7 7 7 7 7 7 7
[
  {
    "kind": "ets",
    "handle": "1:",
    "root": true,
    "refcnt": 2,
    "offloaded": true,
    "options": {
      "bands": 8,
      "strict": 3,
      "quanta": [1514, 1514, 1514, 1514, 1514],
      "priomap": [0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7]
    }
  }
]
Signed-off-by: Petr Machata <petrm@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
		
	
			
		
			
				
	
	
		
			343 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 | |
| 
 | |
| /*
 | |
|  * Enhanced Transmission Selection - 802.1Qaz-based Qdisc
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #include "utils.h"
 | |
| #include "tc_util.h"
 | |
| 
 | |
| static void explain(void)
 | |
| {
 | |
| 	fprintf(stderr, "Usage: ... ets [bands NUMBER] [strict NUMBER] [quanta Q1 Q2...] [priomap P1 P2...]\n");
 | |
| }
 | |
| 
 | |
| static void cexplain(void)
 | |
| {
 | |
| 	fprintf(stderr, "Usage: ... ets [quantum Q1]\n");
 | |
| }
 | |
| 
 | |
| static unsigned int parse_quantum(const char *arg)
 | |
| {
 | |
| 	unsigned int quantum;
 | |
| 
 | |
| 	if (get_unsigned(&quantum, arg, 10)) {
 | |
| 		fprintf(stderr, "Illegal \"quanta\" element\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	if (!quantum)
 | |
| 		fprintf(stderr, "\"quanta\" must be > 0\n");
 | |
| 	return quantum;
 | |
| }
 | |
| 
 | |
| static int parse_nbands(const char *arg, __u8 *pnbands, const char *what)
 | |
| {
 | |
| 	unsigned int tmp;
 | |
| 
 | |
| 	if (get_unsigned(&tmp, arg, 10)) {
 | |
| 		fprintf(stderr, "Illegal \"%s\"\n", what);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (tmp > TCQ_ETS_MAX_BANDS) {
 | |
| 		fprintf(stderr, "The number of \"%s\" must be <= %d\n",
 | |
| 			what, TCQ_ETS_MAX_BANDS);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	*pnbands = tmp;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ets_parse_opt(struct qdisc_util *qu, int argc, char **argv,
 | |
| 			 struct nlmsghdr *n, const char *dev)
 | |
| {
 | |
| 	__u8 nbands = 0;
 | |
| 	__u8 nstrict = 0;
 | |
| 	bool quanta_mode = false;
 | |
| 	unsigned int nquanta = 0;
 | |
| 	__u32 quanta[TCQ_ETS_MAX_BANDS];
 | |
| 	bool priomap_mode = false;
 | |
| 	unsigned int nprio = 0;
 | |
| 	__u8 priomap[TC_PRIO_MAX + 1];
 | |
| 	unsigned int tmp;
 | |
| 	struct rtattr *tail, *nest;
 | |
| 
 | |
| 	while (argc > 0) {
 | |
| 		if (strcmp(*argv, "bands") == 0) {
 | |
| 			if (nbands) {
 | |
| 				fprintf(stderr, "Duplicate \"bands\"\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 			NEXT_ARG();
 | |
| 			if (parse_nbands(*argv, &nbands, "bands"))
 | |
| 				return -1;
 | |
| 			priomap_mode = quanta_mode = false;
 | |
| 		} else if (strcmp(*argv, "strict") == 0) {
 | |
| 			if (nstrict) {
 | |
| 				fprintf(stderr, "Duplicate \"strict\"\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 			NEXT_ARG();
 | |
| 			if (parse_nbands(*argv, &nstrict, "strict"))
 | |
| 				return -1;
 | |
| 			priomap_mode = quanta_mode = false;
 | |
| 		} else if (strcmp(*argv, "quanta") == 0) {
 | |
| 			if (nquanta) {
 | |
| 				fprintf(stderr, "Duplicate \"quanta\"\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 			NEXT_ARG();
 | |
| 			priomap_mode = false;
 | |
| 			quanta_mode = true;
 | |
| 			goto parse_quantum;
 | |
| 		} else if (strcmp(*argv, "priomap") == 0) {
 | |
| 			if (nprio) {
 | |
| 				fprintf(stderr, "Duplicate \"priomap\"\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 			NEXT_ARG();
 | |
| 			priomap_mode = true;
 | |
| 			quanta_mode = false;
 | |
| 			goto parse_priomap;
 | |
| 		} else if (strcmp(*argv, "help") == 0) {
 | |
| 			explain();
 | |
| 			return -1;
 | |
| 		} else if (quanta_mode) {
 | |
| 			unsigned int quantum;
 | |
| 
 | |
| parse_quantum:
 | |
| 			quantum = parse_quantum(*argv);
 | |
| 			if (!quantum)
 | |
| 				return -1;
 | |
| 			quanta[nquanta++] = quantum;
 | |
| 		} else if (priomap_mode) {
 | |
| 			unsigned int band;
 | |
| 
 | |
| parse_priomap:
 | |
| 			if (get_unsigned(&band, *argv, 10)) {
 | |
| 				fprintf(stderr, "Illegal \"priomap\" element\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 			if (nprio > TC_PRIO_MAX) {
 | |
| 				fprintf(stderr, "\"priomap\" index cannot be higher than %u\n", TC_PRIO_MAX);
 | |
| 				return -1;
 | |
| 			}
 | |
| 			priomap[nprio++] = band;
 | |
| 		} else {
 | |
| 			fprintf(stderr, "What is \"%s\"?\n", *argv);
 | |
| 			explain();
 | |
| 			return -1;
 | |
| 		}
 | |
| 		argc--; argv++;
 | |
| 	}
 | |
| 
 | |
| 	if (!nbands)
 | |
| 		nbands = nquanta + nstrict;
 | |
| 	if (!nbands) {
 | |
| 		fprintf(stderr, "One of \"bands\", \"quanta\" or \"strict\" needs to be specified\n");
 | |
| 		explain();
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (nbands < 1) {
 | |
| 		fprintf(stderr, "The number of \"bands\" must be >= 1\n");
 | |
| 		explain();
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (nstrict + nquanta > nbands) {
 | |
| 		fprintf(stderr, "Not enough total bands to cover all the strict bands and quanta\n");
 | |
| 		explain();
 | |
| 		return -1;
 | |
| 	}
 | |
| 	for (tmp = 0; tmp < nprio; tmp++) {
 | |
| 		if (priomap[tmp] >= nbands) {
 | |
| 			fprintf(stderr, "\"priomap\" element is out of bounds\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	tail = addattr_nest(n, 1024, TCA_OPTIONS | NLA_F_NESTED);
 | |
| 	addattr_l(n, 1024, TCA_ETS_NBANDS, &nbands, sizeof(nbands));
 | |
| 	if (nstrict)
 | |
| 		addattr_l(n, 1024, TCA_ETS_NSTRICT, &nstrict, sizeof(nstrict));
 | |
| 	if (nquanta) {
 | |
| 		nest = addattr_nest(n, 1024, TCA_ETS_QUANTA | NLA_F_NESTED);
 | |
| 		for (tmp = 0; tmp < nquanta; tmp++)
 | |
| 			addattr_l(n, 1024, TCA_ETS_QUANTA_BAND,
 | |
| 				  &quanta[tmp], sizeof(quanta[0]));
 | |
| 		addattr_nest_end(n, nest);
 | |
| 	}
 | |
| 	if (nprio) {
 | |
| 		nest = addattr_nest(n, 1024, TCA_ETS_PRIOMAP | NLA_F_NESTED);
 | |
| 		for (tmp = 0; tmp < nprio; tmp++)
 | |
| 			addattr_l(n, 1024, TCA_ETS_PRIOMAP_BAND,
 | |
| 				  &priomap[tmp], sizeof(priomap[0]));
 | |
| 		addattr_nest_end(n, nest);
 | |
| 	}
 | |
| 	addattr_nest_end(n, tail);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ets_parse_copt(struct qdisc_util *qu, int argc, char **argv,
 | |
| 			  struct nlmsghdr *n, const char *dev)
 | |
| {
 | |
| 	unsigned int quantum = 0;
 | |
| 	struct rtattr *tail;
 | |
| 
 | |
| 	while (argc > 0) {
 | |
| 		if (strcmp(*argv, "quantum") == 0) {
 | |
| 			if (quantum) {
 | |
| 				fprintf(stderr, "Duplicate \"quantum\"\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 			NEXT_ARG();
 | |
| 			quantum = parse_quantum(*argv);
 | |
| 			if (!quantum)
 | |
| 				return -1;
 | |
| 		} else if (strcmp(*argv, "help") == 0) {
 | |
| 			cexplain();
 | |
| 			return -1;
 | |
| 		} else {
 | |
| 			fprintf(stderr, "What is \"%s\"?\n", *argv);
 | |
| 			cexplain();
 | |
| 			return -1;
 | |
| 		}
 | |
| 		argc--; argv++;
 | |
| 	}
 | |
| 
 | |
| 	tail = addattr_nest(n, 1024, TCA_OPTIONS | NLA_F_NESTED);
 | |
| 	if (quantum)
 | |
| 		addattr_l(n, 1024, TCA_ETS_QUANTA_BAND, &quantum,
 | |
| 			  sizeof(quantum));
 | |
| 	addattr_nest_end(n, tail);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ets_print_opt_quanta(struct rtattr *opt)
 | |
| {
 | |
| 	int len = RTA_PAYLOAD(opt);
 | |
| 	unsigned int offset;
 | |
| 
 | |
| 	open_json_array(PRINT_ANY, "quanta");
 | |
| 	for (offset = 0; offset < len; ) {
 | |
| 		struct rtattr *tb[TCA_ETS_MAX + 1] = {NULL};
 | |
| 		struct rtattr *attr;
 | |
| 		__u32 quantum;
 | |
| 
 | |
| 		attr = RTA_DATA(opt) + offset;
 | |
| 		parse_rtattr(tb, TCA_ETS_MAX, attr, len - offset);
 | |
| 		offset += RTA_LENGTH(RTA_PAYLOAD(attr));
 | |
| 
 | |
| 		if (!tb[TCA_ETS_QUANTA_BAND]) {
 | |
| 			fprintf(stderr, "No ETS band quantum\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		quantum = rta_getattr_u32(tb[TCA_ETS_QUANTA_BAND]);
 | |
| 		print_uint(PRINT_ANY, NULL, " %u", quantum);
 | |
| 
 | |
| 	}
 | |
| 	close_json_array(PRINT_ANY, " ");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ets_print_opt_priomap(struct rtattr *opt)
 | |
| {
 | |
| 	int len = RTA_PAYLOAD(opt);
 | |
| 	unsigned int offset;
 | |
| 
 | |
| 	open_json_array(PRINT_ANY, "priomap");
 | |
| 	for (offset = 0; offset < len; ) {
 | |
| 		struct rtattr *tb[TCA_ETS_MAX + 1] = {NULL};
 | |
| 		struct rtattr *attr;
 | |
| 		__u8 band;
 | |
| 
 | |
| 		attr = RTA_DATA(opt) + offset;
 | |
| 		parse_rtattr(tb, TCA_ETS_MAX, attr, len - offset);
 | |
| 		offset += RTA_LENGTH(RTA_PAYLOAD(attr)) + 3 /* padding */;
 | |
| 
 | |
| 		if (!tb[TCA_ETS_PRIOMAP_BAND]) {
 | |
| 			fprintf(stderr, "No ETS priomap band\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		band = rta_getattr_u8(tb[TCA_ETS_PRIOMAP_BAND]);
 | |
| 		print_uint(PRINT_ANY, NULL, " %u", band);
 | |
| 
 | |
| 	}
 | |
| 	close_json_array(PRINT_ANY, " ");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ets_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 | |
| {
 | |
| 	struct rtattr *tb[TCA_ETS_MAX + 1];
 | |
| 	__u8 nbands;
 | |
| 	__u8 nstrict;
 | |
| 	int err;
 | |
| 
 | |
| 	if (opt == NULL)
 | |
| 		return 0;
 | |
| 
 | |
| 	parse_rtattr_nested(tb, TCA_ETS_MAX, opt);
 | |
| 
 | |
| 	if (!tb[TCA_ETS_NBANDS] || !tb[TCA_ETS_PRIOMAP]) {
 | |
| 		fprintf(stderr, "Incomplete ETS options\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	nbands = rta_getattr_u8(tb[TCA_ETS_NBANDS]);
 | |
| 	print_uint(PRINT_ANY, "bands", "bands %u ", nbands);
 | |
| 
 | |
| 	if (tb[TCA_ETS_NSTRICT]) {
 | |
| 		nstrict = rta_getattr_u8(tb[TCA_ETS_NSTRICT]);
 | |
| 		print_uint(PRINT_ANY, "strict", "strict %u ", nstrict);
 | |
| 	}
 | |
| 
 | |
| 	if (tb[TCA_ETS_QUANTA]) {
 | |
| 		err = ets_print_opt_quanta(tb[TCA_ETS_QUANTA]);
 | |
| 		if (err)
 | |
| 			return err;
 | |
| 	}
 | |
| 
 | |
| 	return ets_print_opt_priomap(tb[TCA_ETS_PRIOMAP]);
 | |
| }
 | |
| 
 | |
| static int ets_print_copt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 | |
| {
 | |
| 	struct rtattr *tb[TCA_ETS_MAX + 1];
 | |
| 	__u32 quantum;
 | |
| 
 | |
| 	if (opt == NULL)
 | |
| 		return 0;
 | |
| 
 | |
| 	parse_rtattr_nested(tb, TCA_ETS_MAX, opt);
 | |
| 
 | |
| 	if (tb[TCA_ETS_QUANTA_BAND]) {
 | |
| 		quantum = rta_getattr_u32(tb[TCA_ETS_QUANTA_BAND]);
 | |
| 		print_uint(PRINT_ANY, "quantum", "quantum %u ", quantum);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| struct qdisc_util ets_qdisc_util = {
 | |
| 	.id		= "ets",
 | |
| 	.parse_qopt	= ets_parse_opt,
 | |
| 	.parse_copt	= ets_parse_copt,
 | |
| 	.print_qopt	= ets_print_opt,
 | |
| 	.print_copt	= ets_print_copt,
 | |
| };
 |