diff options
author | Alexandre Bounine <alexandre.bounine@idt.com> | 2010-05-26 17:43:58 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-27 12:12:50 -0400 |
commit | 818a04a0bb93643d57dd8935815de2ff307b58a3 (patch) | |
tree | 8b21086dea9e3667bdb8f0843a3b50464d61e62f | |
parent | 07590ff03935a2efbc03bc7861f20c059576a479 (diff) |
rapidio: add switch locking during discovery
Add switch access locking during RapidIO discovery. Access lock is
required when reading switch routing table contents due to indexed
mechanism of RT addressing.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Tested-by: Thomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/rapidio/rio-scan.c | 164 |
1 files changed, 144 insertions, 20 deletions
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 7f1a675d835d..cbf0d5f4fba8 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c | |||
@@ -457,12 +457,87 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport) | |||
457 | } | 457 | } |
458 | 458 | ||
459 | /** | 459 | /** |
460 | * rio_lock_device - Acquires host device lock for specified device | ||
461 | * @port: Master port to send transaction | ||
462 | * @destid: Destination ID for device/switch | ||
463 | * @hopcount: Hopcount to reach switch | ||
464 | * @wait_ms: Max wait time in msec (0 = no timeout) | ||
465 | * | ||
466 | * Attepts to acquire host device lock for specified device | ||
467 | * Returns 0 if device lock acquired or EINVAL if timeout expires. | ||
468 | */ | ||
469 | static int | ||
470 | rio_lock_device(struct rio_mport *port, u16 destid, u8 hopcount, int wait_ms) | ||
471 | { | ||
472 | u32 result; | ||
473 | int tcnt = 0; | ||
474 | |||
475 | /* Attempt to acquire device lock */ | ||
476 | rio_mport_write_config_32(port, destid, hopcount, | ||
477 | RIO_HOST_DID_LOCK_CSR, port->host_deviceid); | ||
478 | rio_mport_read_config_32(port, destid, hopcount, | ||
479 | RIO_HOST_DID_LOCK_CSR, &result); | ||
480 | |||
481 | while (result != port->host_deviceid) { | ||
482 | if (wait_ms != 0 && tcnt == wait_ms) { | ||
483 | pr_debug("RIO: timeout when locking device %x:%x\n", | ||
484 | destid, hopcount); | ||
485 | return -EINVAL; | ||
486 | } | ||
487 | |||
488 | /* Delay a bit */ | ||
489 | mdelay(1); | ||
490 | tcnt++; | ||
491 | /* Try to acquire device lock again */ | ||
492 | rio_mport_write_config_32(port, destid, | ||
493 | hopcount, | ||
494 | RIO_HOST_DID_LOCK_CSR, | ||
495 | port->host_deviceid); | ||
496 | rio_mport_read_config_32(port, destid, | ||
497 | hopcount, | ||
498 | RIO_HOST_DID_LOCK_CSR, &result); | ||
499 | } | ||
500 | |||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | /** | ||
505 | * rio_unlock_device - Releases host device lock for specified device | ||
506 | * @port: Master port to send transaction | ||
507 | * @destid: Destination ID for device/switch | ||
508 | * @hopcount: Hopcount to reach switch | ||
509 | * | ||
510 | * Returns 0 if device lock released or EINVAL if fails. | ||
511 | */ | ||
512 | static int | ||
513 | rio_unlock_device(struct rio_mport *port, u16 destid, u8 hopcount) | ||
514 | { | ||
515 | u32 result; | ||
516 | |||
517 | /* Release device lock */ | ||
518 | rio_mport_write_config_32(port, destid, | ||
519 | hopcount, | ||
520 | RIO_HOST_DID_LOCK_CSR, | ||
521 | port->host_deviceid); | ||
522 | rio_mport_read_config_32(port, destid, hopcount, | ||
523 | RIO_HOST_DID_LOCK_CSR, &result); | ||
524 | if ((result & 0xffff) != 0xffff) { | ||
525 | pr_debug("RIO: badness when releasing device lock %x:%x\n", | ||
526 | destid, hopcount); | ||
527 | return -EINVAL; | ||
528 | } | ||
529 | |||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | /** | ||
460 | * rio_route_add_entry- Add a route entry to a switch routing table | 534 | * rio_route_add_entry- Add a route entry to a switch routing table |
461 | * @mport: Master port to send transaction | 535 | * @mport: Master port to send transaction |
462 | * @rswitch: Switch device | 536 | * @rswitch: Switch device |
463 | * @table: Routing table ID | 537 | * @table: Routing table ID |
464 | * @route_destid: Destination ID to be routed | 538 | * @route_destid: Destination ID to be routed |
465 | * @route_port: Port number to be routed | 539 | * @route_port: Port number to be routed |
540 | * @lock: lock switch device flag | ||
466 | * | 541 | * |
467 | * Calls the switch specific add_entry() method to add a route entry | 542 | * Calls the switch specific add_entry() method to add a route entry |
468 | * on a switch. The route table can be specified using the @table | 543 | * on a switch. The route table can be specified using the @table |
@@ -471,12 +546,26 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport) | |||
471 | * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL | 546 | * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL |
472 | * on failure. | 547 | * on failure. |
473 | */ | 548 | */ |
474 | static int rio_route_add_entry(struct rio_mport *mport, struct rio_switch *rswitch, | 549 | static int |
475 | u16 table, u16 route_destid, u8 route_port) | 550 | rio_route_add_entry(struct rio_mport *mport, struct rio_switch *rswitch, |
551 | u16 table, u16 route_destid, u8 route_port, int lock) | ||
476 | { | 552 | { |
477 | return rswitch->add_entry(mport, rswitch->destid, | 553 | int rc; |
554 | |||
555 | if (lock) { | ||
556 | rc = rio_lock_device(mport, rswitch->destid, | ||
557 | rswitch->hopcount, 1000); | ||
558 | if (rc) | ||
559 | return rc; | ||
560 | } | ||
561 | |||
562 | rc = rswitch->add_entry(mport, rswitch->destid, | ||
478 | rswitch->hopcount, table, | 563 | rswitch->hopcount, table, |
479 | route_destid, route_port); | 564 | route_destid, route_port); |
565 | if (lock) | ||
566 | rio_unlock_device(mport, rswitch->destid, rswitch->hopcount); | ||
567 | |||
568 | return rc; | ||
480 | } | 569 | } |
481 | 570 | ||
482 | /** | 571 | /** |
@@ -486,6 +575,7 @@ static int rio_route_add_entry(struct rio_mport *mport, struct rio_switch *rswit | |||
486 | * @table: Routing table ID | 575 | * @table: Routing table ID |
487 | * @route_destid: Destination ID to be routed | 576 | * @route_destid: Destination ID to be routed |
488 | * @route_port: Pointer to read port number into | 577 | * @route_port: Pointer to read port number into |
578 | * @lock: lock switch device flag | ||
489 | * | 579 | * |
490 | * Calls the switch specific get_entry() method to read a route entry | 580 | * Calls the switch specific get_entry() method to read a route entry |
491 | * in a switch. The route table can be specified using the @table | 581 | * in a switch. The route table can be specified using the @table |
@@ -496,11 +586,24 @@ static int rio_route_add_entry(struct rio_mport *mport, struct rio_switch *rswit | |||
496 | */ | 586 | */ |
497 | static int | 587 | static int |
498 | rio_route_get_entry(struct rio_mport *mport, struct rio_switch *rswitch, u16 table, | 588 | rio_route_get_entry(struct rio_mport *mport, struct rio_switch *rswitch, u16 table, |
499 | u16 route_destid, u8 * route_port) | 589 | u16 route_destid, u8 *route_port, int lock) |
500 | { | 590 | { |
501 | return rswitch->get_entry(mport, rswitch->destid, | 591 | int rc; |
592 | |||
593 | if (lock) { | ||
594 | rc = rio_lock_device(mport, rswitch->destid, | ||
595 | rswitch->hopcount, 1000); | ||
596 | if (rc) | ||
597 | return rc; | ||
598 | } | ||
599 | |||
600 | rc = rswitch->get_entry(mport, rswitch->destid, | ||
502 | rswitch->hopcount, table, | 601 | rswitch->hopcount, table, |
503 | route_destid, route_port); | 602 | route_destid, route_port); |
603 | if (lock) | ||
604 | rio_unlock_device(mport, rswitch->destid, rswitch->hopcount); | ||
605 | |||
606 | return rc; | ||
504 | } | 607 | } |
505 | 608 | ||
506 | /** | 609 | /** |
@@ -640,14 +743,14 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, | |||
640 | sw_inport = rio_get_swpinfo_inport(port, | 743 | sw_inport = rio_get_swpinfo_inport(port, |
641 | RIO_ANY_DESTID(port->sys_size), hopcount); | 744 | RIO_ANY_DESTID(port->sys_size), hopcount); |
642 | rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE, | 745 | rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE, |
643 | port->host_deviceid, sw_inport); | 746 | port->host_deviceid, sw_inport, 0); |
644 | rdev->rswitch->route_table[port->host_deviceid] = sw_inport; | 747 | rdev->rswitch->route_table[port->host_deviceid] = sw_inport; |
645 | 748 | ||
646 | for (destid = 0; destid < next_destid; destid++) { | 749 | for (destid = 0; destid < next_destid; destid++) { |
647 | if (destid == port->host_deviceid) | 750 | if (destid == port->host_deviceid) |
648 | continue; | 751 | continue; |
649 | rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE, | 752 | rio_route_add_entry(port, rdev->rswitch, RIO_GLOBAL_TABLE, |
650 | destid, sw_inport); | 753 | destid, sw_inport, 0); |
651 | rdev->rswitch->route_table[destid] = sw_inport; | 754 | rdev->rswitch->route_table[destid] = sw_inport; |
652 | } | 755 | } |
653 | 756 | ||
@@ -673,7 +776,7 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, | |||
673 | rio_route_add_entry(port, rdev->rswitch, | 776 | rio_route_add_entry(port, rdev->rswitch, |
674 | RIO_GLOBAL_TABLE, | 777 | RIO_GLOBAL_TABLE, |
675 | RIO_ANY_DESTID(port->sys_size), | 778 | RIO_ANY_DESTID(port->sys_size), |
676 | port_num); | 779 | port_num, 0); |
677 | 780 | ||
678 | if (rio_enum_peer(net, port, hopcount + 1) < 0) | 781 | if (rio_enum_peer(net, port, hopcount + 1) < 0) |
679 | return -1; | 782 | return -1; |
@@ -687,7 +790,8 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, | |||
687 | rio_route_add_entry(port, rdev->rswitch, | 790 | rio_route_add_entry(port, rdev->rswitch, |
688 | RIO_GLOBAL_TABLE, | 791 | RIO_GLOBAL_TABLE, |
689 | destid, | 792 | destid, |
690 | port_num); | 793 | port_num, |
794 | 0); | ||
691 | rdev->rswitch-> | 795 | rdev->rswitch-> |
692 | route_table[destid] = | 796 | route_table[destid] = |
693 | port_num; | 797 | port_num; |
@@ -778,17 +882,21 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid, | |||
778 | pr_debug( | 882 | pr_debug( |
779 | "RIO: scanning device on port %d\n", | 883 | "RIO: scanning device on port %d\n", |
780 | port_num); | 884 | port_num); |
885 | |||
886 | rio_lock_device(port, destid, hopcount, 1000); | ||
887 | |||
781 | for (ndestid = 0; | 888 | for (ndestid = 0; |
782 | ndestid < RIO_ANY_DESTID(port->sys_size); | 889 | ndestid < RIO_ANY_DESTID(port->sys_size); |
783 | ndestid++) { | 890 | ndestid++) { |
784 | rio_route_get_entry(port, rdev->rswitch, | 891 | rio_route_get_entry(port, rdev->rswitch, |
785 | RIO_GLOBAL_TABLE, | 892 | RIO_GLOBAL_TABLE, |
786 | ndestid, | 893 | ndestid, |
787 | &route_port); | 894 | &route_port, 0); |
788 | if (route_port == port_num) | 895 | if (route_port == port_num) |
789 | break; | 896 | break; |
790 | } | 897 | } |
791 | 898 | ||
899 | rio_unlock_device(port, destid, hopcount); | ||
792 | if (rio_disc_peer | 900 | if (rio_disc_peer |
793 | (net, port, ndestid, hopcount + 1) < 0) | 901 | (net, port, ndestid, hopcount + 1) < 0) |
794 | return -1; | 902 | return -1; |
@@ -889,7 +997,9 @@ static void rio_update_route_tables(struct rio_mport *port) | |||
889 | rswitch->destid, rswitch->hopcount); | 997 | rswitch->destid, rswitch->hopcount); |
890 | 998 | ||
891 | if (rswitch->add_entry) { | 999 | if (rswitch->add_entry) { |
892 | rio_route_add_entry(port, rswitch, RIO_GLOBAL_TABLE, destid, sport); | 1000 | rio_route_add_entry(port, rswitch, |
1001 | RIO_GLOBAL_TABLE, destid, | ||
1002 | sport, 0); | ||
893 | rswitch->route_table[destid] = sport; | 1003 | rswitch->route_table[destid] = sport; |
894 | } | 1004 | } |
895 | } | 1005 | } |
@@ -963,15 +1073,22 @@ static void rio_build_route_tables(void) | |||
963 | u8 sport; | 1073 | u8 sport; |
964 | 1074 | ||
965 | list_for_each_entry(rdev, &rio_devices, global_list) | 1075 | list_for_each_entry(rdev, &rio_devices, global_list) |
966 | if (rio_is_switch(rdev)) | 1076 | if (rio_is_switch(rdev)) { |
967 | for (i = 0; | 1077 | rio_lock_device(rdev->net->hport, rdev->rswitch->destid, |
968 | i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size); | 1078 | rdev->rswitch->hopcount, 1000); |
969 | i++) { | 1079 | for (i = 0; |
970 | if (rio_route_get_entry | 1080 | i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size); |
971 | (rdev->net->hport, rdev->rswitch, RIO_GLOBAL_TABLE, | 1081 | i++) { |
972 | i, &sport) < 0) | 1082 | if (rio_route_get_entry |
973 | continue; | 1083 | (rdev->net->hport, rdev->rswitch, |
974 | rdev->rswitch->route_table[i] = sport; | 1084 | RIO_GLOBAL_TABLE, i, &sport, 0) < 0) |
1085 | continue; | ||
1086 | rdev->rswitch->route_table[i] = sport; | ||
1087 | } | ||
1088 | |||
1089 | rio_unlock_device(rdev->net->hport, | ||
1090 | rdev->rswitch->destid, | ||
1091 | rdev->rswitch->hopcount); | ||
975 | } | 1092 | } |
976 | } | 1093 | } |
977 | 1094 | ||
@@ -1030,6 +1147,13 @@ int __devinit rio_disc_mport(struct rio_mport *mport) | |||
1030 | del_timer_sync(&rio_enum_timer); | 1147 | del_timer_sync(&rio_enum_timer); |
1031 | 1148 | ||
1032 | pr_debug("done\n"); | 1149 | pr_debug("done\n"); |
1150 | |||
1151 | /* Read DestID assigned by enumerator */ | ||
1152 | rio_local_read_config_32(mport, RIO_DID_CSR, | ||
1153 | &mport->host_deviceid); | ||
1154 | mport->host_deviceid = RIO_GET_DID(mport->sys_size, | ||
1155 | mport->host_deviceid); | ||
1156 | |||
1033 | if (rio_disc_peer(net, mport, RIO_ANY_DESTID(mport->sys_size), | 1157 | if (rio_disc_peer(net, mport, RIO_ANY_DESTID(mport->sys_size), |
1034 | 0) < 0) { | 1158 | 0) < 0) { |
1035 | printk(KERN_INFO | 1159 | printk(KERN_INFO |