netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: sfeldma@gmail.com
To: netdev@vger.kernel.org
Cc: jiri@resnulli.us, roopa@cumulusnetworks.com, linux@roeck-us.net,
	f.fainelli@gmail.com, andrew@lunn.ch, simon.horman@netronome.com,
	joe@perches.com, sridhar.samudrala@intel.com,
	ronen.arad@intel.com
Subject: [PATCH net-next v6 03/23] switchdev: introduce get/set attrs ops
Date: Sat,  9 May 2015 10:40:05 -0700	[thread overview]
Message-ID: <1431193225-807-4-git-send-email-sfeldma@gmail.com> (raw)
In-Reply-To: <1431193225-807-1-git-send-email-sfeldma@gmail.com>

From: Scott Feldman <sfeldma@gmail.com>

Add two new swdev ops for get/set switch port attributes.  Most swdev
interactions on a port are gets or sets on port attributes, so rather than
adding ops for each attribute, let's define clean get/set ops for all
attributes, and then we can have clear, consistent rules on how attributes
propagate on stacked devs.

Add the basic algorithms for get/set attr ops.  Use the same recusive algo
to walk lower devs we've used for STP updates, for example.  For get,
compare attr value for each lower dev and only return success if attr
values match across all lower devs.  For sets, set the same attr value for
all lower devs.  We'll use a two-phase prepare-commit transaction model for
sets.  In the first phase, the driver(s) are asked if attr set is OK.  If
all OK, the commit attr set in second phase.  A driver would NACK the
prepare phase if it can't set the attr due to lack of resources or support,
within it's control.  RTNL lock must be held across both phases because
we'll recurse all lower devs first in prepare phase, and then recurse all
lower devs again in commit phase.  If any lower dev fails the prepare
phase, we need to abort the transaction for all lower devs.

If lower dev recusion isn't desired, allow a flag SWITCHDEV_F_NO_RECURSE to
indicate get/set only work on port (lowest) device.

Signed-off-by: Scott Feldman <sfeldma@gmail.com>
Acked-by: Jiri Pirko <jiri@resnulli.us>
---
 include/net/switchdev.h   |   43 ++++++++++++
 net/switchdev/switchdev.c |  169 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 212 insertions(+)

diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 97b556d..2820438 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -14,6 +14,25 @@
 #include <linux/netdevice.h>
 #include <linux/notifier.h>
 
+#define SWITCHDEV_F_NO_RECURSE		BIT(0)
+
+enum switchdev_trans {
+	SWITCHDEV_TRANS_NONE,
+	SWITCHDEV_TRANS_PREPARE,
+	SWITCHDEV_TRANS_ABORT,
+	SWITCHDEV_TRANS_COMMIT,
+};
+
+enum switchdev_attr_id {
+	SWITCHDEV_ATTR_UNDEFINED,
+};
+
+struct switchdev_attr {
+	enum switchdev_attr_id id;
+	enum switchdev_trans trans;
+	u32 flags;
+};
+
 struct fib_info;
 
 /**
@@ -23,6 +42,10 @@ struct fib_info;
  *   is part of.  If driver implements this, it indicates that it
  *   represents a port of a switch chip.
  *
+ * @switchdev_port_attr_get: Get a port attribute (see switchdev_attr).
+ *
+ * @switchdev_port_attr_set: Set a port attribute (see switchdev_attr).
+ *
  * @switchdev_port_stp_update: Called to notify switch device port of bridge
  *   port STP state change.
  *
@@ -33,6 +56,10 @@ struct fib_info;
 struct switchdev_ops {
 	int	(*switchdev_parent_id_get)(struct net_device *dev,
 					   struct netdev_phys_item_id *psid);
+	int	(*switchdev_port_attr_get)(struct net_device *dev,
+					   struct switchdev_attr *attr);
+	int	(*switchdev_port_attr_set)(struct net_device *dev,
+					   struct switchdev_attr *attr);
 	int	(*switchdev_port_stp_update)(struct net_device *dev, u8 state);
 	int	(*switchdev_fib_ipv4_add)(struct net_device *dev, __be32 dst,
 					  int dst_len, struct fib_info *fi,
@@ -68,6 +95,10 @@ switchdev_notifier_info_to_dev(const struct switchdev_notifier_info *info)
 
 int switchdev_parent_id_get(struct net_device *dev,
 			    struct netdev_phys_item_id *psid);
+int switchdev_port_attr_get(struct net_device *dev,
+			    struct switchdev_attr *attr);
+int switchdev_port_attr_set(struct net_device *dev,
+			    struct switchdev_attr *attr);
 int switchdev_port_stp_update(struct net_device *dev, u8 state);
 int register_switchdev_notifier(struct notifier_block *nb);
 int unregister_switchdev_notifier(struct notifier_block *nb);
@@ -95,6 +126,18 @@ static inline int switchdev_parent_id_get(struct net_device *dev,
 	return -EOPNOTSUPP;
 }
 
+static inline int switchdev_port_attr_get(struct net_device *dev,
+					  struct switchdev_attr *attr)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int switchdev_port_attr_set(struct net_device *dev,
+					  struct switchdev_attr *attr)
+{
+	return -EOPNOTSUPP;
+}
+
 static inline int switchdev_port_stp_update(struct net_device *dev,
 					    u8 state)
 {
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index b7f44a2..8f47187 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -37,6 +37,175 @@ int switchdev_parent_id_get(struct net_device *dev,
 EXPORT_SYMBOL_GPL(switchdev_parent_id_get);
 
 /**
+ *	switchdev_port_attr_get - Get port attribute
+ *
+ *	@dev: port device
+ *	@attr: attribute to get
+ */
+int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr)
+{
+	const struct switchdev_ops *ops = dev->switchdev_ops;
+	struct net_device *lower_dev;
+	struct list_head *iter;
+	struct switchdev_attr first = {
+		.id = SWITCHDEV_ATTR_UNDEFINED
+	};
+	int err = -EOPNOTSUPP;
+
+	if (ops && ops->switchdev_port_attr_get)
+		return ops->switchdev_port_attr_get(dev, attr);
+
+	if (attr->flags & SWITCHDEV_F_NO_RECURSE)
+		return err;
+
+	/* Switch device port(s) may be stacked under
+	 * bond/team/vlan dev, so recurse down to get attr on
+	 * each port.  Return -ENODATA if attr values don't
+	 * compare across ports.
+	 */
+
+	netdev_for_each_lower_dev(dev, lower_dev, iter) {
+		err = switchdev_port_attr_get(lower_dev, attr);
+		if (err)
+			break;
+		if (first.id == SWITCHDEV_ATTR_UNDEFINED)
+			first = *attr;
+		else if (memcmp(&first, attr, sizeof(*attr)))
+			return -ENODATA;
+	}
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(switchdev_port_attr_get);
+
+static int __switchdev_port_attr_set(struct net_device *dev,
+				     struct switchdev_attr *attr)
+{
+	const struct switchdev_ops *ops = dev->switchdev_ops;
+	struct net_device *lower_dev;
+	struct list_head *iter;
+	int err = -EOPNOTSUPP;
+
+	if (ops && ops->switchdev_port_attr_set)
+		return ops->switchdev_port_attr_set(dev, attr);
+
+	if (attr->flags & SWITCHDEV_F_NO_RECURSE)
+		return err;
+
+	/* Switch device port(s) may be stacked under
+	 * bond/team/vlan dev, so recurse down to set attr on
+	 * each port.
+	 */
+
+	netdev_for_each_lower_dev(dev, lower_dev, iter) {
+		err = __switchdev_port_attr_set(lower_dev, attr);
+		if (err)
+			break;
+	}
+
+	return err;
+}
+
+struct switchdev_attr_set_work {
+	struct work_struct work;
+	struct net_device *dev;
+	struct switchdev_attr attr;
+};
+
+static void switchdev_port_attr_set_work(struct work_struct *work)
+{
+	struct switchdev_attr_set_work *asw =
+		container_of(work, struct switchdev_attr_set_work, work);
+	int err;
+
+	rtnl_lock();
+	err = switchdev_port_attr_set(asw->dev, &asw->attr);
+	BUG_ON(err);
+	rtnl_unlock();
+
+	dev_put(asw->dev);
+	kfree(work);
+}
+
+static int switchdev_port_attr_set_defer(struct net_device *dev,
+					 struct switchdev_attr *attr)
+{
+	struct switchdev_attr_set_work *asw;
+
+	asw = kmalloc(sizeof(*asw), GFP_ATOMIC);
+	if (!asw)
+		return -ENOMEM;
+
+	INIT_WORK(&asw->work, switchdev_port_attr_set_work);
+
+	dev_hold(dev);
+	asw->dev = dev;
+	memcpy(&asw->attr, attr, sizeof(asw->attr));
+
+	schedule_work(&asw->work);
+
+	return 0;
+}
+
+/**
+ *	switchdev_port_attr_set - Set port attribute
+ *
+ *	@dev: port device
+ *	@attr: attribute to set
+ *
+ *	Use a 2-phase prepare-commit transaction model to ensure
+ *	system is not left in a partially updated state due to
+ *	failure from driver/device.
+ */
+int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr)
+{
+	int err;
+
+	if (!rtnl_is_locked()) {
+		/* Running prepare-commit transaction across stacked
+		 * devices requires nothing moves, so if rtnl_lock is
+		 * not held, schedule a worker thread to hold rtnl_lock
+		 * while setting attr.
+		 */
+
+		return switchdev_port_attr_set_defer(dev, attr);
+	}
+
+	/* Phase I: prepare for attr set. Driver/device should fail
+	 * here if there are going to be issues in the commit phase,
+	 * such as lack of resources or support.  The driver/device
+	 * should reserve resources needed for the commit phase here,
+	 * but should not commit the attr.
+	 */
+
+	attr->trans = SWITCHDEV_TRANS_PREPARE;
+	err = __switchdev_port_attr_set(dev, attr);
+	if (err) {
+		/* Prepare phase failed: abort the transaction.  Any
+		 * resources reserved in the prepare phase are
+		 * released.
+		 */
+
+		attr->trans = SWITCHDEV_TRANS_ABORT;
+		__switchdev_port_attr_set(dev, attr);
+
+		return err;
+	}
+
+	/* Phase II: commit attr set.  This cannot fail as a fault
+	 * of driver/device.  If it does, it's a bug in the driver/device
+	 * because the driver said everythings was OK in phase I.
+	 */
+
+	attr->trans = SWITCHDEV_TRANS_COMMIT;
+	err = __switchdev_port_attr_set(dev, attr);
+	BUG_ON(err);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(switchdev_port_attr_set);
+
+/**
  *	switchdev_port_stp_update - Notify switch device port of STP
  *					state change
  *	@dev: port device
-- 
1.7.10.4

  parent reply	other threads:[~2015-05-09 17:43 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-09 17:40 [PATCH net-next v6 00/23] switchdev: spring cleanup sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 01/23] switchdev: s/netdev_switch_/switchdev_/ and s/NETDEV_SWITCH_/SWITCHDEV_/ sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 02/23] switchdev: s/swdev_/switchdev_/ sfeldma
2015-05-09 17:40 ` sfeldma [this message]
2015-05-09 17:40 ` [PATCH net-next v6 04/23] switchdev: convert parent_id_get to switchdev attr get sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 05/23] rocker: support prepare-commit transaction model sfeldma
2015-05-09 18:18   ` Jiri Pirko
2015-05-10  6:14     ` Scott Feldman
2015-05-09 17:40 ` [PATCH net-next v6 06/23] switchdev: convert STP update to switchdev attr set sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 07/23] switchdev: introduce switchdev add/del obj ops sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 08/23] switchdev: add port vlan obj sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 09/23] rocker: use switchdev add/del obj for bridge port vlans sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 10/23] switchdev: add bridge port flags attr sfeldma
2015-05-09 18:47   ` Jiri Pirko
2015-05-09 17:40 ` [PATCH net-next v6 11/23] switchdev: add new switchdev bridge setlink sfeldma
2015-05-09 18:48   ` Jiri Pirko
2015-05-09 17:40 ` [PATCH net-next v6 12/23] switchdev: cut over to new switchdev_port_bridge_setlink sfeldma
2015-05-09 18:49   ` Jiri Pirko
2015-05-09 17:40 ` [PATCH net-next v6 13/23] switchdev: remove old switchdev_port_bridge_setlink sfeldma
2015-05-09 18:49   ` Jiri Pirko
2015-05-09 17:40 ` [PATCH net-next v6 14/23] bridge: restore br_setlink back to original sfeldma
2015-05-09 19:00   ` Jiri Pirko
2015-05-10 16:10     ` roopa
2015-05-10 23:55       ` Scott Feldman
2015-05-11  0:55         ` roopa
2015-05-11  2:46           ` Scott Feldman
2015-05-11  3:03             ` roopa
2015-05-09 17:40 ` [PATCH net-next v6 15/23] switchdev: add new switchdev_port_bridge_dellink sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 16/23] switchdev: cut over to " sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 17/23] switchdev: remove unused switchdev_port_bridge_dellink sfeldma
2015-05-09 18:54   ` Jiri Pirko
2015-05-09 17:40 ` [PATCH net-next v6 18/23] switchdev: add new switchdev_port_bridge_getlink sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 19/23] switchdev: cut over to " sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 20/23] switchdev: convert fib_ipv4_add/del over to switchdev_port_obj_add/del sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 21/23] switchdev: remove NETIF_F_HW_SWITCH_OFFLOAD feature flag sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 22/23] rocker: make checkpatch -f clean sfeldma
2015-05-09 17:40 ` [PATCH net-next v6 23/23] switchdev: bring documentation up-to-date sfeldma

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1431193225-807-4-git-send-email-sfeldma@gmail.com \
    --to=sfeldma@gmail.com \
    --cc=andrew@lunn.ch \
    --cc=f.fainelli@gmail.com \
    --cc=jiri@resnulli.us \
    --cc=joe@perches.com \
    --cc=linux@roeck-us.net \
    --cc=netdev@vger.kernel.org \
    --cc=ronen.arad@intel.com \
    --cc=roopa@cumulusnetworks.com \
    --cc=simon.horman@netronome.com \
    --cc=sridhar.samudrala@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).