diff mbox series

[13/28] thunderbolt: Add helper function to iterate from one port to another

Message ID 20190129150143.12681-14-mika.westerberg@linux.intel.com
State Not Applicable
Delegated to: David Miller
Headers show
Series thunderbolt: Software connection manager improvements | expand

Commit Message

Mika Westerberg Jan. 29, 2019, 3:01 p.m. UTC
We need to be able to walk from one port to another when we are creating
paths where there are multiple switches between two ports. For this
reason introduce a new function tb_port_get_next() and a new macro
tb_for_each_port().

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c | 60 ++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.h     |  6 ++++
 2 files changed, 66 insertions(+)
diff mbox series

Patch

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index ae366c252c74..d0f9e3a01851 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -683,6 +683,66 @@  void tb_port_release_out_hopid(struct tb_port *port, int hopid)
 	ida_simple_remove(&port->out_hopids, hopid);
 }
 
+/**
+ * tb_port_get_next() - Return next port for given port
+ * @start: Start port of the walk
+ * @end: End port of the walk
+ * @prev: Previous port (%NULL if this is the first)
+ *
+ * This function can be used to walk from one port to another if they
+ * are connected through zero or more switches. If the @prev is dual
+ * link port, the function follows that link and returns another end on
+ * that same link.
+ *
+ * If the walk cannot be continued, returns %NULL.
+ *
+ * Domain tb->lock must be held when this function is called.
+ */
+struct tb_port *tb_port_get_next(struct tb_port *start, struct tb_port *end,
+				 struct tb_port *prev)
+{
+	struct tb_port *port, *next;
+
+	if (!prev)
+		return start;
+
+	if (prev->sw == end->sw) {
+		if (prev != end)
+			return end;
+		return NULL;
+	}
+
+	/* Switch back to use primary links for walking */
+	if (prev->dual_link_port && prev->link_nr)
+		port = prev->dual_link_port;
+	else
+		port = prev;
+
+	if (start->sw->config.depth < end->sw->config.depth) {
+		if (port->remote &&
+		    port->remote->sw->config.depth > port->sw->config.depth)
+			next = port->remote;
+		else
+			next = tb_port_at(tb_route(end->sw), port->sw);
+	} else if (start->sw->config.depth > end->sw->config.depth) {
+		if (tb_is_upstream_port(port))
+			next = port->remote;
+		else
+			next = tb_upstream_port(port->sw);
+	} else {
+		/* Must be the same switch then */
+		if (start->sw != end->sw)
+			return NULL;
+		return end;
+	}
+
+	/* If prev was dual link return another end of that link then */
+	if (next->dual_link_port && next->link_nr != prev->link_nr)
+		return next->dual_link_port;
+
+	return next;
+}
+
 /**
  * tb_pci_port_enable() - Enable PCIe adapter port
  * @port: PCIe port to enable
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index a4f835c4bdc2..360333caa3c4 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -448,6 +448,12 @@  int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_in_hopid(struct tb_port *port, int hopid);
 int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_out_hopid(struct tb_port *port, int hopid);
+struct tb_port *tb_port_get_next(struct tb_port *start, struct tb_port *end,
+				 struct tb_port *prev);
+
+#define tb_for_each_port(port, start, end)			\
+	for (port = tb_port_get_next(start, end, NULL); port;	\
+	     port = tb_port_get_next(start, end, port))
 
 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);