Commit c70555b0 authored by Alexandre Bounine's avatar Alexandre Bounine Committed by Linus Torvalds

[PATCH] rapidio: fix multi-switch enumeration

This patch contains two fixes for RapisIO enumeration logic:

1. Fix enumeration in configurations with multiple switches. The patch adds:

   a. Enumeration of an empty switch.  Empty switch is a switch that
      does not have any endpoint devices attached to it (except host device
      or previous switch in a chain).  New code assigns a phony destination
      ID associated with the switch and sets up corresponding routes.

   b. Adds a second pass to the enumeration to setup routes to
      devices discovered after switch was scanned.

2. Fix enumeration failure when riohdid parameter has non-zero value.
   Current version fails to setup response path to the host when it has
   destination ID other that 0.
Signed-off-by: default avatarAlexandre Bounine <alexandreb@tundra.com>
Acked-by: default avatarMatt Porter <mporter@kernel.crashing.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d5698c28
...@@ -326,14 +326,17 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, ...@@ -326,14 +326,17 @@ static struct rio_dev *rio_setup_device(struct rio_net *net,
rio_mport_read_config_32(port, destid, hopcount, RIO_DST_OPS_CAR, rio_mport_read_config_32(port, destid, hopcount, RIO_DST_OPS_CAR,
&rdev->dst_ops); &rdev->dst_ops);
if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops) if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops)) {
&& do_enum) { if (do_enum) {
rio_set_device_id(port, destid, hopcount, next_destid); rio_set_device_id(port, destid, hopcount, next_destid);
rdev->destid = next_destid++; rdev->destid = next_destid++;
if (next_destid == port->host_deviceid) if (next_destid == port->host_deviceid)
next_destid++; next_destid++;
} else
rdev->destid = rio_get_device_id(port, destid, hopcount);
} else } else
rdev->destid = rio_get_device_id(port, destid, hopcount); /* Switch device has an associated destID */
rdev->destid = RIO_INVALID_DESTID;
/* If a PE has both switch and other functions, show it as a switch */ /* If a PE has both switch and other functions, show it as a switch */
if (rio_is_switch(rdev)) { if (rio_is_switch(rdev)) {
...@@ -347,7 +350,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, ...@@ -347,7 +350,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net,
} }
rswitch->switchid = next_switchid; rswitch->switchid = next_switchid;
rswitch->hopcount = hopcount; rswitch->hopcount = hopcount;
rswitch->destid = 0xffff; rswitch->destid = destid;
/* Initialize switch route table */ /* Initialize switch route table */
for (rdid = 0; rdid < RIO_MAX_ROUTE_ENTRIES; rdid++) for (rdid = 0; rdid < RIO_MAX_ROUTE_ENTRIES; rdid++)
rswitch->route_table[rdid] = RIO_INVALID_ROUTE; rswitch->route_table[rdid] = RIO_INVALID_ROUTE;
...@@ -422,7 +425,7 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport) ...@@ -422,7 +425,7 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport)
/** /**
* rio_route_add_entry- Add a route entry to a switch routing table * rio_route_add_entry- Add a route entry to a switch routing table
* @mport: Master port to send transaction * @mport: Master port to send transaction
* @rdev: Switch device * @rswitch: Switch device
* @table: Routing table ID * @table: Routing table ID
* @route_destid: Destination ID to be routed * @route_destid: Destination ID to be routed
* @route_port: Port number to be routed * @route_port: Port number to be routed
...@@ -434,18 +437,18 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport) ...@@ -434,18 +437,18 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport)
* %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL
* on failure. * on failure.
*/ */
static int rio_route_add_entry(struct rio_mport *mport, struct rio_dev *rdev, static int rio_route_add_entry(struct rio_mport *mport, struct rio_switch *rswitch,
u16 table, u16 route_destid, u8 route_port) u16 table, u16 route_destid, u8 route_port)
{ {
return rdev->rswitch->add_entry(mport, rdev->rswitch->destid, return rswitch->add_entry(mport, rswitch->destid,
rdev->rswitch->hopcount, table, rswitch->hopcount, table,
route_destid, route_port); route_destid, route_port);
} }
/** /**
* rio_route_get_entry- Read a route entry in a switch routing table * rio_route_get_entry- Read a route entry in a switch routing table
* @mport: Master port to send transaction * @mport: Master port to send transaction
* @rdev: Switch device * @rswitch: Switch device
* @table: Routing table ID * @table: Routing table ID
* @route_destid: Destination ID to be routed * @route_destid: Destination ID to be routed
* @route_port: Pointer to read port number into * @route_port: Pointer to read port number into
...@@ -458,11 +461,11 @@ static int rio_route_add_entry(struct rio_mport *mport, struct rio_dev *rdev, ...@@ -458,11 +461,11 @@ static int rio_route_add_entry(struct rio_mport *mport, struct rio_dev *rdev,
* on failure. * on failure.
*/ */
static int static int
rio_route_get_entry(struct rio_mport *mport, struct rio_dev *rdev, u16 table, rio_route_get_entry(struct rio_mport *mport, struct rio_switch *rswitch, u16 table,
u16 route_destid, u8 * route_port) u16 route_destid, u8 * route_port)
{ {
return rdev->rswitch->get_entry(mport, rdev->rswitch->destid, return rswitch->get_entry(mport, rswitch->destid,
rdev->rswitch->hopcount, table, rswitch->hopcount, table,
route_destid, route_port); route_destid, route_port);
} }
...@@ -552,6 +555,8 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port, ...@@ -552,6 +555,8 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
int port_num; int port_num;
int num_ports; int num_ports;
int cur_destid; int cur_destid;
int sw_destid;
int sw_inport;
struct rio_dev *rdev; struct rio_dev *rdev;
u16 destid; u16 destid;
int tmp; int tmp;
...@@ -594,15 +599,17 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port, ...@@ -594,15 +599,17 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
if (rio_is_switch(rdev)) { if (rio_is_switch(rdev)) {
next_switchid++; next_switchid++;
sw_inport = rio_get_swpinfo_inport(port, RIO_ANY_DESTID, hopcount);
rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE,
port->host_deviceid, sw_inport);
rdev->rswitch->route_table[port->host_deviceid] = sw_inport;
for (destid = 0; destid < next_destid; destid++) { for (destid = 0; destid < next_destid; destid++) {
rio_route_add_entry(port, rdev, RIO_GLOBAL_TABLE, if (destid == port->host_deviceid)
destid, rio_get_swpinfo_inport(port, continue;
RIO_ANY_DESTID, rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE,
hopcount)); destid, sw_inport);
rdev->rswitch->route_table[destid] = rdev->rswitch->route_table[destid] = sw_inport;
rio_get_swpinfo_inport(port, RIO_ANY_DESTID,
hopcount);
} }
num_ports = num_ports =
...@@ -610,9 +617,9 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port, ...@@ -610,9 +617,9 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
pr_debug( pr_debug(
"RIO: found %s (vid %4.4x did %4.4x) with %d ports\n", "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
rio_name(rdev), rdev->vid, rdev->did, num_ports); rio_name(rdev), rdev->vid, rdev->did, num_ports);
sw_destid = next_destid;
for (port_num = 0; port_num < num_ports; port_num++) { for (port_num = 0; port_num < num_ports; port_num++) {
if (rio_get_swpinfo_inport if (sw_inport == port_num)
(port, RIO_ANY_DESTID, hopcount) == port_num)
continue; continue;
cur_destid = next_destid; cur_destid = next_destid;
...@@ -622,7 +629,7 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port, ...@@ -622,7 +629,7 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
pr_debug( pr_debug(
"RIO: scanning device on port %d\n", "RIO: scanning device on port %d\n",
port_num); port_num);
rio_route_add_entry(port, rdev, rio_route_add_entry(port, rdev->rswitch,
RIO_GLOBAL_TABLE, RIO_GLOBAL_TABLE,
RIO_ANY_DESTID, port_num); RIO_ANY_DESTID, port_num);
...@@ -633,7 +640,9 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port, ...@@ -633,7 +640,9 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
if (next_destid > cur_destid) { if (next_destid > cur_destid) {
for (destid = cur_destid; for (destid = cur_destid;
destid < next_destid; destid++) { destid < next_destid; destid++) {
rio_route_add_entry(port, rdev, if (destid == port->host_deviceid)
continue;
rio_route_add_entry(port, rdev->rswitch,
RIO_GLOBAL_TABLE, RIO_GLOBAL_TABLE,
destid, destid,
port_num); port_num);
...@@ -641,10 +650,18 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port, ...@@ -641,10 +650,18 @@ static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
route_table[destid] = route_table[destid] =
port_num; port_num;
} }
rdev->rswitch->destid = cur_destid;
} }
} }
} }
/* Check for empty switch */
if (next_destid == sw_destid) {
next_destid++;
if (next_destid == port->host_deviceid)
next_destid++;
}
rdev->rswitch->destid = sw_destid;
} else } else
pr_debug("RIO: found %s (vid %4.4x did %4.4x)\n", pr_debug("RIO: found %s (vid %4.4x did %4.4x)\n",
rio_name(rdev), rdev->vid, rdev->did); rio_name(rdev), rdev->vid, rdev->did);
...@@ -721,7 +738,7 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid, ...@@ -721,7 +738,7 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
port_num); port_num);
for (ndestid = 0; ndestid < RIO_ANY_DESTID; for (ndestid = 0; ndestid < RIO_ANY_DESTID;
ndestid++) { ndestid++) {
rio_route_get_entry(port, rdev, rio_route_get_entry(port, rdev->rswitch,
RIO_GLOBAL_TABLE, RIO_GLOBAL_TABLE,
ndestid, ndestid,
&route_port); &route_port);
...@@ -797,6 +814,44 @@ static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port) ...@@ -797,6 +814,44 @@ static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port)
return net; return net;
} }
/**
* rio_update_route_tables- Updates route tables in switches
* @port: Master port associated with the RIO network
*
* For each enumerated device, ensure that each switch in a system
* has correct routing entries. Add routes for devices that where
* unknown dirung the first enumeration pass through the switch.
*/
static void rio_update_route_tables(struct rio_mport *port)
{
struct rio_dev *rdev;
struct rio_switch *rswitch;
u8 sport;
u16 destid;
list_for_each_entry(rdev, &rio_devices, global_list) {
destid = (rio_is_switch(rdev))?rdev->rswitch->destid:rdev->destid;
list_for_each_entry(rswitch, &rio_switches, node) {
if (rio_is_switch(rdev) && (rdev->rswitch == rswitch))
continue;
if (RIO_INVALID_ROUTE == rswitch->route_table[destid]) {
sport = rio_get_swpinfo_inport(port,
rswitch->destid, rswitch->hopcount);
if (rswitch->add_entry) {
rio_route_add_entry(port, rswitch, RIO_GLOBAL_TABLE, destid, sport);
rswitch->route_table[destid] = sport;
}
}
}
}
}
/** /**
* rio_enum_mport- Start enumeration through a master port * rio_enum_mport- Start enumeration through a master port
* @mport: Master port to send transactions * @mport: Master port to send transactions
...@@ -838,6 +893,7 @@ int rio_enum_mport(struct rio_mport *mport) ...@@ -838,6 +893,7 @@ int rio_enum_mport(struct rio_mport *mport)
rc = -EBUSY; rc = -EBUSY;
goto out; goto out;
} }
rio_update_route_tables(mport);
rio_clear_locks(mport); rio_clear_locks(mport);
} else { } else {
printk(KERN_INFO "RIO: master port %d link inactive\n", printk(KERN_INFO "RIO: master port %d link inactive\n",
...@@ -865,8 +921,8 @@ static void rio_build_route_tables(void) ...@@ -865,8 +921,8 @@ static void rio_build_route_tables(void)
if (rio_is_switch(rdev)) if (rio_is_switch(rdev))
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++) { for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++) {
if (rio_route_get_entry if (rio_route_get_entry
(rdev->net->hport, rdev, RIO_GLOBAL_TABLE, i, (rdev->net->hport, rdev->rswitch, RIO_GLOBAL_TABLE,
&sport) < 0) i, &sport) < 0)
continue; continue;
rdev->rswitch->route_table[i] = sport; rdev->rswitch->route_table[i] = sport;
} }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#define RIO_ANY_DESTID 0xff #define RIO_ANY_DESTID 0xff
#define RIO_NO_HOPCOUNT -1 #define RIO_NO_HOPCOUNT -1
#define RIO_INVALID_DESTID 0xffff
#define RIO_MAX_MPORT_RESOURCES 16 #define RIO_MAX_MPORT_RESOURCES 16
#define RIO_MAX_DEV_RESOURCES 16 #define RIO_MAX_DEV_RESOURCES 16
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment