diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firewire/fw-sbp2.c | 126 |
1 files changed, 122 insertions, 4 deletions
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 6d10934c58f1..ea4811c45512 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c | |||
@@ -139,6 +139,7 @@ struct sbp2_logical_unit { | |||
139 | int generation; | 139 | int generation; |
140 | int retries; | 140 | int retries; |
141 | struct delayed_work work; | 141 | struct delayed_work work; |
142 | bool blocked; | ||
142 | }; | 143 | }; |
143 | 144 | ||
144 | /* | 145 | /* |
@@ -157,6 +158,9 @@ struct sbp2_target { | |||
157 | int address_high; | 158 | int address_high; |
158 | unsigned int workarounds; | 159 | unsigned int workarounds; |
159 | unsigned int mgt_orb_timeout; | 160 | unsigned int mgt_orb_timeout; |
161 | |||
162 | int dont_block; /* counter for each logical unit */ | ||
163 | int blocked; /* ditto */ | ||
160 | }; | 164 | }; |
161 | 165 | ||
162 | /* | 166 | /* |
@@ -646,6 +650,107 @@ static void sbp2_agent_reset_no_wait(struct sbp2_logical_unit *lu) | |||
646 | &z, sizeof(z), complete_agent_reset_write_no_wait, t); | 650 | &z, sizeof(z), complete_agent_reset_write_no_wait, t); |
647 | } | 651 | } |
648 | 652 | ||
653 | static void sbp2_set_generation(struct sbp2_logical_unit *lu, int generation) | ||
654 | { | ||
655 | struct fw_card *card = fw_device(lu->tgt->unit->device.parent)->card; | ||
656 | unsigned long flags; | ||
657 | |||
658 | /* serialize with comparisons of lu->generation and card->generation */ | ||
659 | spin_lock_irqsave(&card->lock, flags); | ||
660 | lu->generation = generation; | ||
661 | spin_unlock_irqrestore(&card->lock, flags); | ||
662 | } | ||
663 | |||
664 | static inline void sbp2_allow_block(struct sbp2_logical_unit *lu) | ||
665 | { | ||
666 | /* | ||
667 | * We may access dont_block without taking card->lock here: | ||
668 | * All callers of sbp2_allow_block() and all callers of sbp2_unblock() | ||
669 | * are currently serialized against each other. | ||
670 | * And a wrong result in sbp2_conditionally_block()'s access of | ||
671 | * dont_block is rather harmless, it simply misses its first chance. | ||
672 | */ | ||
673 | --lu->tgt->dont_block; | ||
674 | } | ||
675 | |||
676 | /* | ||
677 | * Blocks lu->tgt if all of the following conditions are met: | ||
678 | * - Login, INQUIRY, and high-level SCSI setup of all of the target's | ||
679 | * logical units have been finished (indicated by dont_block == 0). | ||
680 | * - lu->generation is stale. | ||
681 | * | ||
682 | * Note, scsi_block_requests() must be called while holding card->lock, | ||
683 | * otherwise it might foil sbp2_[conditionally_]unblock()'s attempt to | ||
684 | * unblock the target. | ||
685 | */ | ||
686 | static void sbp2_conditionally_block(struct sbp2_logical_unit *lu) | ||
687 | { | ||
688 | struct sbp2_target *tgt = lu->tgt; | ||
689 | struct fw_card *card = fw_device(tgt->unit->device.parent)->card; | ||
690 | struct Scsi_Host *shost = | ||
691 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); | ||
692 | unsigned long flags; | ||
693 | |||
694 | spin_lock_irqsave(&card->lock, flags); | ||
695 | if (!tgt->dont_block && !lu->blocked && | ||
696 | lu->generation != card->generation) { | ||
697 | lu->blocked = true; | ||
698 | if (++tgt->blocked == 1) { | ||
699 | scsi_block_requests(shost); | ||
700 | fw_notify("blocked %s\n", lu->tgt->bus_id); | ||
701 | } | ||
702 | } | ||
703 | spin_unlock_irqrestore(&card->lock, flags); | ||
704 | } | ||
705 | |||
706 | /* | ||
707 | * Unblocks lu->tgt as soon as all its logical units can be unblocked. | ||
708 | * Note, it is harmless to run scsi_unblock_requests() outside the | ||
709 | * card->lock protected section. On the other hand, running it inside | ||
710 | * the section might clash with shost->host_lock. | ||
711 | */ | ||
712 | static void sbp2_conditionally_unblock(struct sbp2_logical_unit *lu) | ||
713 | { | ||
714 | struct sbp2_target *tgt = lu->tgt; | ||
715 | struct fw_card *card = fw_device(tgt->unit->device.parent)->card; | ||
716 | struct Scsi_Host *shost = | ||
717 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); | ||
718 | unsigned long flags; | ||
719 | bool unblock = false; | ||
720 | |||
721 | spin_lock_irqsave(&card->lock, flags); | ||
722 | if (lu->blocked && lu->generation == card->generation) { | ||
723 | lu->blocked = false; | ||
724 | unblock = --tgt->blocked == 0; | ||
725 | } | ||
726 | spin_unlock_irqrestore(&card->lock, flags); | ||
727 | |||
728 | if (unblock) { | ||
729 | scsi_unblock_requests(shost); | ||
730 | fw_notify("unblocked %s\n", lu->tgt->bus_id); | ||
731 | } | ||
732 | } | ||
733 | |||
734 | /* | ||
735 | * Prevents future blocking of tgt and unblocks it. | ||
736 | * Note, it is harmless to run scsi_unblock_requests() outside the | ||
737 | * card->lock protected section. On the other hand, running it inside | ||
738 | * the section might clash with shost->host_lock. | ||
739 | */ | ||
740 | static void sbp2_unblock(struct sbp2_target *tgt) | ||
741 | { | ||
742 | struct fw_card *card = fw_device(tgt->unit->device.parent)->card; | ||
743 | struct Scsi_Host *shost = | ||
744 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); | ||
745 | unsigned long flags; | ||
746 | |||
747 | spin_lock_irqsave(&card->lock, flags); | ||
748 | ++tgt->dont_block; | ||
749 | spin_unlock_irqrestore(&card->lock, flags); | ||
750 | |||
751 | scsi_unblock_requests(shost); | ||
752 | } | ||
753 | |||
649 | static void sbp2_release_target(struct kref *kref) | 754 | static void sbp2_release_target(struct kref *kref) |
650 | { | 755 | { |
651 | struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref); | 756 | struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref); |
@@ -653,6 +758,9 @@ static void sbp2_release_target(struct kref *kref) | |||
653 | struct Scsi_Host *shost = | 758 | struct Scsi_Host *shost = |
654 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); | 759 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); |
655 | 760 | ||
761 | /* prevent deadlocks */ | ||
762 | sbp2_unblock(tgt); | ||
763 | |||
656 | list_for_each_entry_safe(lu, next, &tgt->lu_list, link) { | 764 | list_for_each_entry_safe(lu, next, &tgt->lu_list, link) { |
657 | if (lu->sdev) | 765 | if (lu->sdev) |
658 | scsi_remove_device(lu->sdev); | 766 | scsi_remove_device(lu->sdev); |
@@ -717,17 +825,20 @@ static void sbp2_login(struct work_struct *work) | |||
717 | 825 | ||
718 | if (sbp2_send_management_orb(lu, node_id, generation, | 826 | if (sbp2_send_management_orb(lu, node_id, generation, |
719 | SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) { | 827 | SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) { |
720 | if (lu->retries++ < 5) | 828 | if (lu->retries++ < 5) { |
721 | sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); | 829 | sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); |
722 | else | 830 | } else { |
723 | fw_error("%s: failed to login to LUN %04x\n", | 831 | fw_error("%s: failed to login to LUN %04x\n", |
724 | tgt->bus_id, lu->lun); | 832 | tgt->bus_id, lu->lun); |
833 | /* Let any waiting I/O fail from now on. */ | ||
834 | sbp2_unblock(lu->tgt); | ||
835 | } | ||
725 | goto out; | 836 | goto out; |
726 | } | 837 | } |
727 | 838 | ||
728 | lu->generation = generation; | ||
729 | tgt->node_id = node_id; | 839 | tgt->node_id = node_id; |
730 | tgt->address_high = local_node_id << 16; | 840 | tgt->address_high = local_node_id << 16; |
841 | sbp2_set_generation(lu, generation); | ||
731 | 842 | ||
732 | /* Get command block agent offset and login id. */ | 843 | /* Get command block agent offset and login id. */ |
733 | lu->command_block_agent_address = | 844 | lu->command_block_agent_address = |
@@ -749,6 +860,7 @@ static void sbp2_login(struct work_struct *work) | |||
749 | /* This was a re-login. */ | 860 | /* This was a re-login. */ |
750 | if (lu->sdev) { | 861 | if (lu->sdev) { |
751 | sbp2_cancel_orbs(lu); | 862 | sbp2_cancel_orbs(lu); |
863 | sbp2_conditionally_unblock(lu); | ||
752 | goto out; | 864 | goto out; |
753 | } | 865 | } |
754 | 866 | ||
@@ -785,6 +897,7 @@ static void sbp2_login(struct work_struct *work) | |||
785 | 897 | ||
786 | /* No error during __scsi_add_device() */ | 898 | /* No error during __scsi_add_device() */ |
787 | lu->sdev = sdev; | 899 | lu->sdev = sdev; |
900 | sbp2_allow_block(lu); | ||
788 | goto out; | 901 | goto out; |
789 | 902 | ||
790 | out_logout_login: | 903 | out_logout_login: |
@@ -825,6 +938,8 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) | |||
825 | lu->sdev = NULL; | 938 | lu->sdev = NULL; |
826 | lu->lun = lun_entry & 0xffff; | 939 | lu->lun = lun_entry & 0xffff; |
827 | lu->retries = 0; | 940 | lu->retries = 0; |
941 | lu->blocked = false; | ||
942 | ++tgt->dont_block; | ||
828 | INIT_LIST_HEAD(&lu->orb_list); | 943 | INIT_LIST_HEAD(&lu->orb_list); |
829 | INIT_DELAYED_WORK(&lu->work, sbp2_login); | 944 | INIT_DELAYED_WORK(&lu->work, sbp2_login); |
830 | 945 | ||
@@ -1041,15 +1156,16 @@ static void sbp2_reconnect(struct work_struct *work) | |||
1041 | goto out; | 1156 | goto out; |
1042 | } | 1157 | } |
1043 | 1158 | ||
1044 | lu->generation = generation; | ||
1045 | tgt->node_id = node_id; | 1159 | tgt->node_id = node_id; |
1046 | tgt->address_high = local_node_id << 16; | 1160 | tgt->address_high = local_node_id << 16; |
1161 | sbp2_set_generation(lu, generation); | ||
1047 | 1162 | ||
1048 | fw_notify("%s: reconnected to LUN %04x (%d retries)\n", | 1163 | fw_notify("%s: reconnected to LUN %04x (%d retries)\n", |
1049 | tgt->bus_id, lu->lun, lu->retries); | 1164 | tgt->bus_id, lu->lun, lu->retries); |
1050 | 1165 | ||
1051 | sbp2_agent_reset(lu); | 1166 | sbp2_agent_reset(lu); |
1052 | sbp2_cancel_orbs(lu); | 1167 | sbp2_cancel_orbs(lu); |
1168 | sbp2_conditionally_unblock(lu); | ||
1053 | out: | 1169 | out: |
1054 | sbp2_target_put(tgt); | 1170 | sbp2_target_put(tgt); |
1055 | } | 1171 | } |
@@ -1066,6 +1182,7 @@ static void sbp2_update(struct fw_unit *unit) | |||
1066 | * Iteration over tgt->lu_list is therefore safe here. | 1182 | * Iteration over tgt->lu_list is therefore safe here. |
1067 | */ | 1183 | */ |
1068 | list_for_each_entry(lu, &tgt->lu_list, link) { | 1184 | list_for_each_entry(lu, &tgt->lu_list, link) { |
1185 | sbp2_conditionally_block(lu); | ||
1069 | lu->retries = 0; | 1186 | lu->retries = 0; |
1070 | sbp2_queue_work(lu, 0); | 1187 | sbp2_queue_work(lu, 0); |
1071 | } | 1188 | } |
@@ -1169,6 +1286,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) | |||
1169 | * or when sending the write (less likely). | 1286 | * or when sending the write (less likely). |
1170 | */ | 1287 | */ |
1171 | result = DID_BUS_BUSY << 16; | 1288 | result = DID_BUS_BUSY << 16; |
1289 | sbp2_conditionally_block(orb->lu); | ||
1172 | } | 1290 | } |
1173 | 1291 | ||
1174 | dma_unmap_single(device->card->device, orb->base.request_bus, | 1292 | dma_unmap_single(device->card->device, orb->base.request_bus, |