Subject: [PATCH v4 2/2] [iproute2/tc] hfsc: add link layer overhead adaption

From: Jussi Kivilinna <[email-masked]>
Date: Thu, 10 Jul 2008 22:34:54 +0300

Patch adds 'mpu', 'mtu', 'overhead' and 'linklayer' options to hfsc. These
options are used to create size table for sch_hfsc. Size table is only used
and passed to kernel if these options are used.

Signed-off-by: Jussi Kivilinna <[email-masked]>
---

 include/linux/pkt_sched.h |    5 +
 tc/q_hfsc.c               |  155 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 155 insertions(+), 5 deletions(-)

diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 5bf1444..46db55d 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -303,6 +303,9 @@ struct tc_htb_xstats
 struct tc_hfsc_qopt
 {
 	__u16	defcls;		/* default class */
+	__u16	__reserved;
+	struct tc_sizespec szopts;
+	__u16	stab[512];
 };
 
 struct tc_service_curve
@@ -326,6 +329,8 @@ enum
 	TCA_HFSC_RSC,
 	TCA_HFSC_FSC,
 	TCA_HFSC_USC,
+	TCA_HFSC_SZOPTS,
+	TCA_HFSC_STAB,
 	__TCA_HFSC_MAX,
 };
 
diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c
index b190c71..e8bff6e 100644
--- a/tc/q_hfsc.c
+++ b/tc/q_hfsc.c
@@ -12,6 +12,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stddef.h>
 #include <unistd.h>
 #include <syslog.h>
 #include <fcntl.h>
@@ -24,6 +25,9 @@
 #include "utils.h"
 #include "tc_util.h"
 
+#define endof(type, member) \
+	(offsetof(type, member) + sizeof(((type *)0)->member))
+
 static int hfsc_get_sc(int *, char ***, struct tc_service_curve *);

@@ -31,7 +35,13 @@ static void
 explain_qdisc(void)
 {
 	fprintf(stderr,
-		"Usage: ... hfsc [ default CLASSID ]\n"
+		"Usage: ... hfsc [ default CLASSID ] [ mtu BYTES] [ mpu BYTES ]\n"
+		"                [ overhead BYTES ] [ linklayer TYPE ]\n"
+		"\n"
+		" mtu       : max packet size we create rate map for {2047}\n"
+		" mpu       : minimum packet size used in rate computations\n"
+		" overhead  : per-packet size overhead used in rate computations\n"
+		" linklayer : adapting to a linklayer e.g. atm\n"
 		"\n"
 		" default: default class for unclassified packets\n"
 	);
@@ -42,6 +52,13 @@ explain_class(void)
 {
 	fprintf(stderr,
 		"Usage: ... hfsc [ [ rt SC ] [ ls SC ] | [ sc SC ] ] [ ul SC ]\n"
+		"                [ mtu BYTES] [ mpu BYTES ] [ overhead BYTES ]\n"
+		"                [ linklayer TYPE ]\n"
+		"\n"
+		" mtu       : max packet size we create rate map for {2047}\n"
+		" mpu       : minimum packet size used in rate computations\n"
+		" overhead  : per-packet size overhead used in rate computations\n"
+		" linklayer : adapting to a linklayer e.g. atm\n"
 		"\n"
 		"SC := [ [ m1 BPS ] [ d SEC ] m2 BPS\n"
 		"\n"
@@ -67,14 +84,86 @@ explain1(char *arg)
 }
 
 static int
+hfsc_parse_stab(int *argcp, char ***argvp, unsigned *mtup,
+		unsigned int *linklayerp, short *overheadp,
+		unsigned short *mpup)
+{
+	char **argv = *argvp;
+	int argc = *argcp;
+	int next = 0;
+
+	if (matches(*argv, "mtu") == 0) {
+		NEXT_ARG();
+		if (get_u32(mtup, *argv, 10)) {
+			explain1("mtu");
+			return -1;
+		}
+	} else if (matches(*argv, "mpu") == 0) {
+		NEXT_ARG();
+		if (get_u16(mpup, *argv, 10)) {
+			explain1("mpu");
+			return -1;
+		}
+	} else if (matches(*argv, "overhead") == 0) {
+		NEXT_ARG();
+		if (get_s16(overheadp, *argv, 10)) {
+			explain1("overhead");
+			return -1;
+		}
+	} else if (matches(*argv, "linklayer") == 0) {
+		NEXT_ARG();
+		if (get_linklayer(linklayerp, *argv)) {
+			explain1("linklayer");
+			return -1;
+		}
+	} else {
+		next = 1;
+	}
+
+	*argvp = argv;
+	*argcp = argc;
+	return next;
+}
+
+static int
+hfsc_create_stab(struct tc_sizespec *szoptsp, __u16 *stabp, unsigned mtu,
+			unsigned int linklayer, short overhead,
+			unsigned short mpu)
+{
+	/* Only use stab when needed, mtu only defines stab properties so
+	 * it is not checked here. */
+	if (linklayer != LINKLAYER_ETHERNET || mpu != 0 || overhead != 0) {
+		szoptsp->mpu = mpu;
+		szoptsp->overhead = overhead;
+		if (tc_calc_stable(szoptsp, stabp, -1, mtu, linklayer) < 0) {
+			fprintf(stderr, "HFSC: failed to calculate size table.\n");
+			return -1;
+		}
+		return 1;
+	}
+	return 0;
+}
+
+static int
 hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
 {
 	struct tc_hfsc_qopt qopt;
+	unsigned qopt_len;
+	unsigned mtu = 0;
+	unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+	short overhead = 0;
+	unsigned short mpu = 0;
+	int use_stab, next;
 
 	memset(&qopt, 0, sizeof(qopt));
 
 	while (argc > 0) {
-		if (matches(*argv, "default") == 0) {
+		next = hfsc_parse_stab(&argc, &argv, &mtu, &linklayer,
+					&overhead, &mpu);
+		if (next != 1) {
+			if (next < 0)
+				return next;
+		} else if (matches(*argv, "default") == 0) {
 			NEXT_ARG();
 			if (qopt.defcls != 0) {
 				fprintf(stderr, "HFSC: Double \"default\"\n");
@@ -95,7 +184,15 @@ hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
 		argc--, argv++;
 	}
 
-	addattr_l(n, 1024, TCA_OPTIONS, &qopt, sizeof(qopt));
+	use_stab = hfsc_create_stab(&qopt.szopts, qopt.stab, mtu, linklayer,
+				overhead, mpu);
+	if (use_stab < 0)
+		return use_stab;
+	qopt_len = (use_stab > 0) ? endof(struct tc_hfsc_qopt, stab) :
+					endof(struct tc_hfsc_qopt, defcls);
+
+	addattr_l(n, 2024, TCA_OPTIONS, &qopt, qopt_len);
+
 	return 0;
 }
 
@@ -103,16 +200,27 @@ static int
 hfsc_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 {
 	struct tc_hfsc_qopt *qopt;
+	SPRINT_BUF(b1);
 
 	if (opt == NULL)
 		return 0;
-	if (RTA_PAYLOAD(opt) < sizeof(*qopt))
+	if (RTA_PAYLOAD(opt) < endof(struct tc_hfsc_qopt, defcls))
 		return -1;
 	qopt = RTA_DATA(opt);
 
 	if (qopt->defcls != 0)
 		fprintf(f, "default %x ", qopt->defcls);
 
+	if (show_details && RTA_PAYLOAD(opt) >=
+					endof(struct tc_hfsc_qopt, szopts)) {
+		if (qopt->szopts.mpu)
+			fprintf(f, "mpu %s ",
+				sprint_size(qopt->szopts.mpu, b1));
+		if (qopt->szopts.overhead)
+			fprintf(f, "overhead %s ",
+				sprint_size(qopt->szopts.overhead, b1));
+	}
+
 	return 0;
 }
 
@@ -145,14 +253,28 @@ hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
 	struct tc_service_curve rsc, fsc, usc;
 	int rsc_ok, fsc_ok, usc_ok;
 	struct rtattr *tail;
+	struct tc_sizespec szopts;
+	__u16 stab[512];
+	int use_stab, next;
+	unsigned mtu = 0;
+	unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+	short overhead = 0;
+	unsigned short mpu = 0;
 
 	memset(&rsc, 0, sizeof(rsc));
 	memset(&fsc, 0, sizeof(fsc));
 	memset(&usc, 0, sizeof(usc));
+	memset(&szopts, 0, sizeof(szopts));
+	memset(stab, 0, TCA_HFSC_STAB);
 	rsc_ok = fsc_ok = usc_ok = 0;
 
 	while (argc > 0) {
-		if (matches(*argv, "rt") == 0) {
+		next = hfsc_parse_stab(&argc, &argv, &mtu, &linklayer,
+					&overhead, &mpu);
+		if (next != 1) {
+			if (next < 0)
+				return next;
+		} else if (matches(*argv, "rt") == 0) {
 			NEXT_ARG();
 			if (hfsc_get_sc(&argc, &argv, &rsc) < 0) {
 				explain1("rt");
@@ -205,6 +327,10 @@ hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
 		return -1;
 	}
 
+	use_stab = hfsc_create_stab(&szopts, stab, mtu, linklayer, overhead, mpu);
+	if (use_stab < 0)
+		return use_stab;
+
 	tail = NLMSG_TAIL(n);
 
 	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
@@ -214,6 +340,10 @@ hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
 		addattr_l(n, 1024, TCA_HFSC_FSC, &fsc, sizeof(fsc));
 	if (usc_ok)
 		addattr_l(n, 1024, TCA_HFSC_USC, &usc, sizeof(usc));
+	if (use_stab) {
+		addattr_l(n, 2024, TCA_HFSC_SZOPTS, &szopts, sizeof(szopts));
+		addattr_l(n, 3024, TCA_HFSC_STAB, stab, TC_STAB_SIZE);
+	}
 
 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
 	return 0;
@@ -235,6 +365,8 @@ hfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 {
 	struct rtattr *tb[TCA_HFSC_MAX+1];
 	struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL;
+	struct tc_sizespec *szopts = NULL;
+	SPRINT_BUF(b1);
 
 	if (opt == NULL)
 		return 0;
@@ -259,6 +391,12 @@ hfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 		else
 			usc = RTA_DATA(tb[TCA_HFSC_USC]);
 	}
+	if (tb[TCA_HFSC_SZOPTS]) {
+		if (RTA_PAYLOAD(tb[TCA_HFSC_SZOPTS]) < sizeof(*szopts))
+			fprintf(stderr, "HFSC: truncated rate options\n");
+		else
+			szopts = RTA_DATA(tb[TCA_HFSC_SZOPTS]);
+	}

 	if (rsc != NULL && fsc != NULL &&
@@ -273,6 +411,13 @@ hfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 	if (usc != NULL)
 		hfsc_print_sc(f, "ul", usc);
 
+	if (szopts != NULL && show_details) {
+		if (szopts->mpu)
+			fprintf(f, "mpu %s ", sprint_size(szopts->mpu, b1));
+		if (szopts->overhead)
+			fprintf(f, "overhead %s ", sprint_size(szopts->overhead, b1));
+	}
+
 	return 0;
 }