diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-08-11 05:52:08 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-10-16 17:59:55 -0400 |
commit | 261b5f664c6c68c5209656a71c41823eda0d938b (patch) | |
tree | 52045ab3beb1564b248a8389c241ac825b14c1bd /drivers | |
parent | c4f3d41fed11c9050aa93bbaeed9f7f06bcc93ba (diff) |
ieee1394: sbp2: fix unsafe iteration over list of devices
sbp2_host_reset and sbp2_handle_status_write are not serialized against
sbp2_alloc_device and sbp2_remove_device.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ieee1394/sbp2.c | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index a81ba8fca0db..1b353b964b33 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c | |||
@@ -242,6 +242,8 @@ static int sbp2_max_speed_and_size(struct sbp2_lu *); | |||
242 | 242 | ||
243 | static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xA, 0xB, 0xC }; | 243 | static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xA, 0xB, 0xC }; |
244 | 244 | ||
245 | static DEFINE_RWLOCK(sbp2_hi_logical_units_lock); | ||
246 | |||
245 | static struct hpsb_highlevel sbp2_highlevel = { | 247 | static struct hpsb_highlevel sbp2_highlevel = { |
246 | .name = SBP2_DEVICE_NAME, | 248 | .name = SBP2_DEVICE_NAME, |
247 | .host_reset = sbp2_host_reset, | 249 | .host_reset = sbp2_host_reset, |
@@ -732,6 +734,7 @@ static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud) | |||
732 | struct sbp2_fwhost_info *hi; | 734 | struct sbp2_fwhost_info *hi; |
733 | struct Scsi_Host *shost = NULL; | 735 | struct Scsi_Host *shost = NULL; |
734 | struct sbp2_lu *lu = NULL; | 736 | struct sbp2_lu *lu = NULL; |
737 | unsigned long flags; | ||
735 | 738 | ||
736 | lu = kzalloc(sizeof(*lu), GFP_KERNEL); | 739 | lu = kzalloc(sizeof(*lu), GFP_KERNEL); |
737 | if (!lu) { | 740 | if (!lu) { |
@@ -784,7 +787,9 @@ static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud) | |||
784 | 787 | ||
785 | lu->hi = hi; | 788 | lu->hi = hi; |
786 | 789 | ||
790 | write_lock_irqsave(&sbp2_hi_logical_units_lock, flags); | ||
787 | list_add_tail(&lu->lu_list, &hi->logical_units); | 791 | list_add_tail(&lu->lu_list, &hi->logical_units); |
792 | write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); | ||
788 | 793 | ||
789 | /* Register the status FIFO address range. We could use the same FIFO | 794 | /* Register the status FIFO address range. We could use the same FIFO |
790 | * for targets at different nodes. However we need different FIFOs per | 795 | * for targets at different nodes. However we need different FIFOs per |
@@ -828,16 +833,20 @@ static void sbp2_host_reset(struct hpsb_host *host) | |||
828 | { | 833 | { |
829 | struct sbp2_fwhost_info *hi; | 834 | struct sbp2_fwhost_info *hi; |
830 | struct sbp2_lu *lu; | 835 | struct sbp2_lu *lu; |
836 | unsigned long flags; | ||
831 | 837 | ||
832 | hi = hpsb_get_hostinfo(&sbp2_highlevel, host); | 838 | hi = hpsb_get_hostinfo(&sbp2_highlevel, host); |
833 | if (!hi) | 839 | if (!hi) |
834 | return; | 840 | return; |
841 | |||
842 | read_lock_irqsave(&sbp2_hi_logical_units_lock, flags); | ||
835 | list_for_each_entry(lu, &hi->logical_units, lu_list) | 843 | list_for_each_entry(lu, &hi->logical_units, lu_list) |
836 | if (likely(atomic_read(&lu->state) != | 844 | if (likely(atomic_read(&lu->state) != |
837 | SBP2LU_STATE_IN_SHUTDOWN)) { | 845 | SBP2LU_STATE_IN_SHUTDOWN)) { |
838 | atomic_set(&lu->state, SBP2LU_STATE_IN_RESET); | 846 | atomic_set(&lu->state, SBP2LU_STATE_IN_RESET); |
839 | scsi_block_requests(lu->shost); | 847 | scsi_block_requests(lu->shost); |
840 | } | 848 | } |
849 | read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); | ||
841 | } | 850 | } |
842 | 851 | ||
843 | static int sbp2_start_device(struct sbp2_lu *lu) | 852 | static int sbp2_start_device(struct sbp2_lu *lu) |
@@ -919,6 +928,7 @@ alloc_fail: | |||
919 | static void sbp2_remove_device(struct sbp2_lu *lu) | 928 | static void sbp2_remove_device(struct sbp2_lu *lu) |
920 | { | 929 | { |
921 | struct sbp2_fwhost_info *hi; | 930 | struct sbp2_fwhost_info *hi; |
931 | unsigned long flags; | ||
922 | 932 | ||
923 | if (!lu) | 933 | if (!lu) |
924 | return; | 934 | return; |
@@ -933,7 +943,9 @@ static void sbp2_remove_device(struct sbp2_lu *lu) | |||
933 | flush_scheduled_work(); | 943 | flush_scheduled_work(); |
934 | sbp2util_remove_command_orb_pool(lu, hi->host); | 944 | sbp2util_remove_command_orb_pool(lu, hi->host); |
935 | 945 | ||
946 | write_lock_irqsave(&sbp2_hi_logical_units_lock, flags); | ||
936 | list_del(&lu->lu_list); | 947 | list_del(&lu->lu_list); |
948 | write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); | ||
937 | 949 | ||
938 | if (lu->login_response) | 950 | if (lu->login_response) |
939 | dma_free_coherent(hi->host->device.parent, | 951 | dma_free_coherent(hi->host->device.parent, |
@@ -1707,6 +1719,7 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, | |||
1707 | } | 1719 | } |
1708 | 1720 | ||
1709 | /* Find the unit which wrote the status. */ | 1721 | /* Find the unit which wrote the status. */ |
1722 | read_lock_irqsave(&sbp2_hi_logical_units_lock, flags); | ||
1710 | list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) { | 1723 | list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) { |
1711 | if (lu_tmp->ne->nodeid == nodeid && | 1724 | if (lu_tmp->ne->nodeid == nodeid && |
1712 | lu_tmp->status_fifo_addr == addr) { | 1725 | lu_tmp->status_fifo_addr == addr) { |
@@ -1714,6 +1727,8 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, | |||
1714 | break; | 1727 | break; |
1715 | } | 1728 | } |
1716 | } | 1729 | } |
1730 | read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags); | ||
1731 | |||
1717 | if (unlikely(!lu)) { | 1732 | if (unlikely(!lu)) { |
1718 | SBP2_ERR("lu is NULL - device is gone?"); | 1733 | SBP2_ERR("lu is NULL - device is gone?"); |
1719 | return RCODE_ADDRESS_ERROR; | 1734 | return RCODE_ADDRESS_ERROR; |