lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20080714.160038.25781008.davem@davemloft.net>
Date:	Mon, 14 Jul 2008 16:00:38 -0700 (PDT)
From:	David Miller <davem@...emloft.net>
To:	netdev@...r.kernel.org
Subject: [PATCH 14/14]: pkt_sched: Rewrite tc_ctl_tclass() to be multiqueue
 aware.


Now that delete and change class operations have seperate validate and
commit operations, this is mostly a trivial transformation.

At this point every aspect of the generic networking is pretty much
fully multiqueue aware.

Signed-off-by: David S. Miller <davem@...emloft.net>
---
 net/sched/sch_api.c |  292 +++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 211 insertions(+), 81 deletions(-)

diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index a4020a6..7e4faaa 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1495,132 +1495,262 @@ done:
  *	Traffic classes manipulation.		*
  ************************************************/
 
+struct tc_ctl_op_info {
+	struct Qdisc			*q;
+	u32				qid;
+	u32				clid;
+	unsigned long			cl;
+	unsigned long			new_cl;
+	const struct Qdisc_class_ops	*cops;
+	unsigned int			flags;
+#define TC_CTL_OP_CHANGE_VALIDATED	0x00000001
+};
 
-
-static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+/* parent == TC_H_UNSPEC - unspecified parent.
+ * parent == TC_H_ROOT   - class is root, which has no parent.
+ * parent == X:0	 - parent is root class.
+ * parent == X:Y	 - parent is a node in hierarchy.
+ * parent == 0:Y	 - parent is X:Y, where X:0 is qdisc.
+ *
+ * handle == 0:0	 - generate handle from kernel pool.
+ * handle == 0:Y	 - class is X:Y, where X:0 is qdisc.
+ * handle == X:Y	 - clear.
+ * handle == X:0	 - root class.
+ */
+static int prep_qid_one(struct tc_ctl_op_info *qp, struct netdev_queue *txq,
+			u32 qid, u32 pid, u32 clid)
 {
-	struct net *net = sock_net(skb->sk);
-	struct netdev_queue *dev_queue;
-	struct tcmsg *tcm = NLMSG_DATA(n);
-	struct nlattr *tca[TCA_MAX + 1];
-	struct net_device *dev;
-	struct Qdisc *q = NULL;
-	const struct Qdisc_class_ops *cops;
-	unsigned long cl = 0;
-	unsigned long new_cl;
-	u32 pid = tcm->tcm_parent;
-	u32 clid = tcm->tcm_handle;
-	u32 qid = TC_H_MAJ(clid);
-	int err;
-
-	if (net != &init_net)
-		return -EINVAL;
-
-	if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
-		return -ENODEV;
-
-	err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
-	if (err < 0)
-		return err;
-
-	/*
-	   parent == TC_H_UNSPEC - unspecified parent.
-	   parent == TC_H_ROOT   - class is root, which has no parent.
-	   parent == X:0	 - parent is root class.
-	   parent == X:Y	 - parent is a node in hierarchy.
-	   parent == 0:Y	 - parent is X:Y, where X:0 is qdisc.
-
-	   handle == 0:0	 - generate handle from kernel pool.
-	   handle == 0:Y	 - class is X:Y, where X:0 is qdisc.
-	   handle == X:Y	 - clear.
-	   handle == X:0	 - root class.
-	 */
-
-	/* Step 1. Determine qdisc handle X:0 */
-
-	dev_queue = netdev_get_tx_queue(dev, 0);
 	if (pid != TC_H_ROOT) {
 		u32 qid1 = TC_H_MAJ(pid);
 
 		if (qid && qid1) {
-			/* If both majors are known, they must be identical. */
+			/* If both majors are known, they must be
+			 * identical.
+			 */
 			if (qid != qid1)
 				return -EINVAL;
 		} else if (qid1) {
 			qid = qid1;
 		} else if (qid == 0)
-			qid = dev_queue->qdisc_sleeping->handle;
-
-		/* Now qid is genuine qdisc handle consistent
-		   both with parent and child.
+			qid = txq->qdisc_sleeping->handle;
 
-		   TC_H_MAJ(pid) still may be unspecified, complete it now.
+		/* Now qid is genuine qdisc handle consistent both
+		 * with parent and child.
+		 *
+		 * TC_H_MAJ(pid) still may be unspecified, complete it
+		 * now.
 		 */
 		if (pid)
 			pid = TC_H_MAKE(qid, pid);
 	} else {
 		if (qid == 0)
-			qid = dev_queue->qdisc_sleeping->handle;
+			qid = txq->qdisc_sleeping->handle;
 	}
 
-	/* OK. Locate qdisc */
-	if ((q = qdisc_lookup(dev_queue, qid)) == NULL)
+	qp->qid = qid;
+	qp->q = qdisc_lookup(txq, qid);
+	if (!qp->q)
 		return -ENOENT;
 
-	/* An check that it supports classes */
-	cops = q->ops->cl_ops;
-	if (cops == NULL)
+	qp->cops = qp->q->ops->cl_ops;
+	if (!qp->cops)
 		return -EINVAL;
 
-	/* Now try to get class */
 	if (clid == 0) {
 		if (pid == TC_H_ROOT)
 			clid = qid;
 	} else
 		clid = TC_H_MAKE(qid, clid);
+	qp->clid = clid;
 
-	if (clid)
-		cl = cops->get(q, clid);
+	if (qp->clid)
+		qp->cl = qp->cops->get(qp->q, qp->clid);
 
-	if (cl == 0) {
-		err = -ENOENT;
-		if (n->nlmsg_type != RTM_NEWTCLASS || !(n->nlmsg_flags&NLM_F_CREATE))
-			goto out;
+	return 0;
+}
+
+static void put_classes(struct tc_ctl_op_info *queue_arr, unsigned int num_q)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_q; i++) {
+		struct tc_ctl_op_info *qp = queue_arr + i;
+
+		if (qp->cl)
+			qp->cops->put(qp->q, qp->cl);
+	}
+}
+
+static int prep_qids(struct tc_ctl_op_info *queue_arr, unsigned int num_q,
+		     struct net_device *dev, struct tcmsg *tcm)
+{
+	u32 pid = tcm->tcm_parent;
+	u32 clid = tcm->tcm_handle;
+	u32 qid = TC_H_MAJ(clid);
+	unsigned int i;
+
+	for (i = 0; i < num_q; i++) {
+		struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
+		struct tc_ctl_op_info *qp = queue_arr + i;
+		int err = prep_qid_one(qp, txq, qid, pid, clid);
+
+		if (err) {
+			put_classes(queue_arr, num_q);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void cancel_tc_ops(struct tc_ctl_op_info *queue_arr, unsigned int num_q,
+			  struct tcmsg *tcm, struct nlattr **tca)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_q; i++) {
+		struct tc_ctl_op_info *qp = queue_arr + i;
+
+		if (qp->flags & TC_CTL_OP_CHANGE_VALIDATED)
+			qp->cops->cancel_change(qp->q, qp->clid,
+						tcm->tcm_parent,
+						tca, qp->new_cl);
+	}
+}
+
+static int validate_tc_one(struct tc_ctl_op_info *qp, struct sk_buff *skb,
+			   struct nlmsghdr *n, struct tcmsg *tcm,
+			   struct nlattr **tca)
+{
+	int err;
+
+	if (!qp->cl) {
+		if (n->nlmsg_type != RTM_NEWTCLASS ||
+		    !(n->nlmsg_flags&NLM_F_CREATE))
+			return -ENOENT;
 	} else {
 		switch (n->nlmsg_type) {
 		case RTM_NEWTCLASS:
-			err = -EEXIST;
 			if (n->nlmsg_flags&NLM_F_EXCL)
-				goto out;
+				return -EEXIST;
 			break;
+
 		case RTM_DELTCLASS:
-			err = cops->validate_delete(q, cl);
-			if (!err) {
-				cops->commit_delete(q, cl);
-				tclass_notify(skb, n, q, cl, RTM_DELTCLASS);
-			}
-			goto out;
+			return qp->cops->validate_delete(qp->q, qp->cl);
+
 		case RTM_GETTCLASS:
-			err = tclass_notify(skb, n, q, cl, RTM_NEWTCLASS);
-			goto out;
+			return tclass_notify(skb, n, qp->q, qp->cl,
+					     RTM_NEWTCLASS);
+
 		default:
-			err = -EINVAL;
-			goto out;
+			return -EINVAL;
 		}
 	}
 
-	new_cl = cl;
+	qp->new_cl = qp->cl;
+	err = qp->cops->validate_change(qp->q, qp->clid, tcm->tcm_parent,
+					tca, &qp->new_cl);
+
+	if (!err)
+		qp->flags |= TC_CTL_OP_CHANGE_VALIDATED;
+
+	return err;
+}
+
+static int validate_tc_ops(struct tc_ctl_op_info *queue_arr, unsigned int num_q,
+			   struct sk_buff *skb, struct nlmsghdr *n,
+			   struct tcmsg *tcm, struct nlattr **tca)
+{
+	unsigned int i;
 
-	err = cops->validate_change(q, clid, pid, tca, &new_cl);
-	if (!err) {
-		cops->commit_change(q, clid, pid, tca, new_cl);
-		tclass_notify(skb, n, q, new_cl, RTM_NEWTCLASS);
+	for (i = 0; i < num_q; i++) {
+		struct tc_ctl_op_info *qp = queue_arr + i;
+		int err = validate_tc_one(qp, skb, n, tcm, tca);
+
+		if (err) {
+			cancel_tc_ops(queue_arr, num_q, tcm, tca);
+			return err;
+		}
 	}
+	return 0;
+}
 
-out:
-	if (cl)
-		cops->put(q, cl);
+static void commit_tc_one(struct tc_ctl_op_info *qp, struct sk_buff *skb,
+			  struct nlmsghdr *n, struct tcmsg *tcm,
+			  struct nlattr **tca)
+{
+	if (qp->cl) {
+		switch (n->nlmsg_type) {
+		case RTM_NEWTCLASS:
+			break;
+
+		case RTM_DELTCLASS:
+			qp->cops->commit_delete(qp->q, qp->cl);
+			tclass_notify(skb, n, qp->q, qp->cl, RTM_DELTCLASS);
+			return;
+
+		default:
+			return;
+		}
+	}
+	qp->cops->commit_change(qp->q, qp->clid, tcm->tcm_parent,
+				tca, qp->new_cl);
+	tclass_notify(skb, n, qp->q, qp->new_cl, RTM_NEWTCLASS);
+}
 
+static void commit_tc_ops(struct tc_ctl_op_info *queue_arr, unsigned int num_q,
+			  struct sk_buff *skb, struct nlmsghdr *n,
+			  struct tcmsg *tcm, struct nlattr **tca)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_q; i++)
+		commit_tc_one(queue_arr + i, skb, n, tcm, tca);
+}
+
+static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+{
+	struct net *net = sock_net(skb->sk);
+	struct tcmsg *tcm = NLMSG_DATA(n);
+	struct tc_ctl_op_info *queue_arr;
+	struct nlattr *tca[TCA_MAX + 1];
+	struct net_device *dev;
+	unsigned int num_q;
+	int err;
+
+	if (net != &init_net)
+		return -EINVAL;
+
+	if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
+		return -ENODEV;
+
+	err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
+	if (err < 0)
+		return err;
+
+	num_q = dev->num_tx_queues;
+	queue_arr = kzalloc(sizeof(struct tc_ctl_op_info) * num_q,
+			    GFP_KERNEL);
+	if (!queue_arr)
+		return -ENOMEM;
+
+	err = prep_qids(queue_arr, num_q, dev, tcm);
+	if (err)
+		goto err_free_queue;
+
+	err = validate_tc_ops(queue_arr, num_q, skb, n, tcm, tca);
+	if (err)
+		goto err_put_classes;
+
+	commit_tc_ops(queue_arr, num_q, skb, n, tcm, tca);
+
+	err = 0;
+
+err_put_classes:
+	put_classes(queue_arr, num_q);
+
+err_free_queue:
+	kfree(queue_arr);
 	return err;
 }
 
-- 
1.5.6.2.255.gbed62

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ