diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2008-02-26 17:30:02 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2008-03-02 06:35:46 -0500 |
commit | f8436158b1d76e6842856048f287799468b56eb2 (patch) | |
tree | 45f1b707668819b83fee5a8d82a7c5333b989f0f | |
parent | d395991c117d43bfca97101a931a41d062a93852 (diff) |
firewire: fw-sbp2: better fix for NULL pointer dereference in scsi_remove_device
Patch "firewire: fw-sbp2: fix NULL pointer deref. in scsi_remove_device"
had the unintended effect that firewire-sbp2 could not be unloaded
anymore until all SBP-2 devices were unplugged.
We now fix the NULL pointer bug by reacquiring a reference to the sdev
instead of holding a reference to the sdev (and to the module) all the
time.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Tested-by: Jarod Wilson <jwilson@redhat.com>
-rw-r--r-- | drivers/firewire/fw-sbp2.c | 46 |
1 files changed, 27 insertions, 19 deletions
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 5259491580fc..a093ac329db7 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c | |||
@@ -122,7 +122,6 @@ static const char sbp2_driver_name[] = "sbp2"; | |||
122 | struct sbp2_logical_unit { | 122 | struct sbp2_logical_unit { |
123 | struct sbp2_target *tgt; | 123 | struct sbp2_target *tgt; |
124 | struct list_head link; | 124 | struct list_head link; |
125 | struct scsi_device *sdev; | ||
126 | struct fw_address_handler address_handler; | 125 | struct fw_address_handler address_handler; |
127 | struct list_head orb_list; | 126 | struct list_head orb_list; |
128 | 127 | ||
@@ -139,6 +138,7 @@ struct sbp2_logical_unit { | |||
139 | int generation; | 138 | int generation; |
140 | int retries; | 139 | int retries; |
141 | struct delayed_work work; | 140 | struct delayed_work work; |
141 | bool has_sdev; | ||
142 | bool blocked; | 142 | bool blocked; |
143 | }; | 143 | }; |
144 | 144 | ||
@@ -751,20 +751,33 @@ static void sbp2_unblock(struct sbp2_target *tgt) | |||
751 | scsi_unblock_requests(shost); | 751 | scsi_unblock_requests(shost); |
752 | } | 752 | } |
753 | 753 | ||
754 | static int sbp2_lun2int(u16 lun) | ||
755 | { | ||
756 | struct scsi_lun eight_bytes_lun; | ||
757 | |||
758 | memset(&eight_bytes_lun, 0, sizeof(eight_bytes_lun)); | ||
759 | eight_bytes_lun.scsi_lun[0] = (lun >> 8) & 0xff; | ||
760 | eight_bytes_lun.scsi_lun[1] = lun & 0xff; | ||
761 | |||
762 | return scsilun_to_int(&eight_bytes_lun); | ||
763 | } | ||
764 | |||
754 | static void sbp2_release_target(struct kref *kref) | 765 | static void sbp2_release_target(struct kref *kref) |
755 | { | 766 | { |
756 | struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref); | 767 | struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref); |
757 | struct sbp2_logical_unit *lu, *next; | 768 | struct sbp2_logical_unit *lu, *next; |
758 | struct Scsi_Host *shost = | 769 | struct Scsi_Host *shost = |
759 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); | 770 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); |
771 | struct scsi_device *sdev; | ||
760 | 772 | ||
761 | /* prevent deadlocks */ | 773 | /* prevent deadlocks */ |
762 | sbp2_unblock(tgt); | 774 | sbp2_unblock(tgt); |
763 | 775 | ||
764 | list_for_each_entry_safe(lu, next, &tgt->lu_list, link) { | 776 | list_for_each_entry_safe(lu, next, &tgt->lu_list, link) { |
765 | if (lu->sdev) { | 777 | sdev = scsi_device_lookup(shost, 0, 0, sbp2_lun2int(lu->lun)); |
766 | scsi_remove_device(lu->sdev); | 778 | if (sdev) { |
767 | scsi_device_put(lu->sdev); | 779 | scsi_remove_device(sdev); |
780 | scsi_device_put(sdev); | ||
768 | } | 781 | } |
769 | sbp2_send_management_orb(lu, tgt->node_id, lu->generation, | 782 | sbp2_send_management_orb(lu, tgt->node_id, lu->generation, |
770 | SBP2_LOGOUT_REQUEST, lu->login_id, NULL); | 783 | SBP2_LOGOUT_REQUEST, lu->login_id, NULL); |
@@ -807,7 +820,6 @@ static void sbp2_login(struct work_struct *work) | |||
807 | struct fw_device *device = fw_device(tgt->unit->device.parent); | 820 | struct fw_device *device = fw_device(tgt->unit->device.parent); |
808 | struct Scsi_Host *shost; | 821 | struct Scsi_Host *shost; |
809 | struct scsi_device *sdev; | 822 | struct scsi_device *sdev; |
810 | struct scsi_lun eight_bytes_lun; | ||
811 | struct sbp2_login_response response; | 823 | struct sbp2_login_response response; |
812 | int generation, node_id, local_node_id; | 824 | int generation, node_id, local_node_id; |
813 | 825 | ||
@@ -820,7 +832,7 @@ static void sbp2_login(struct work_struct *work) | |||
820 | local_node_id = device->card->node_id; | 832 | local_node_id = device->card->node_id; |
821 | 833 | ||
822 | /* If this is a re-login attempt, log out, or we might be rejected. */ | 834 | /* If this is a re-login attempt, log out, or we might be rejected. */ |
823 | if (lu->sdev) | 835 | if (lu->has_sdev) |
824 | sbp2_send_management_orb(lu, device->node_id, generation, | 836 | sbp2_send_management_orb(lu, device->node_id, generation, |
825 | SBP2_LOGOUT_REQUEST, lu->login_id, NULL); | 837 | SBP2_LOGOUT_REQUEST, lu->login_id, NULL); |
826 | 838 | ||
@@ -859,7 +871,7 @@ static void sbp2_login(struct work_struct *work) | |||
859 | sbp2_agent_reset(lu); | 871 | sbp2_agent_reset(lu); |
860 | 872 | ||
861 | /* This was a re-login. */ | 873 | /* This was a re-login. */ |
862 | if (lu->sdev) { | 874 | if (lu->has_sdev) { |
863 | sbp2_cancel_orbs(lu); | 875 | sbp2_cancel_orbs(lu); |
864 | sbp2_conditionally_unblock(lu); | 876 | sbp2_conditionally_unblock(lu); |
865 | goto out; | 877 | goto out; |
@@ -868,13 +880,8 @@ static void sbp2_login(struct work_struct *work) | |||
868 | if (lu->tgt->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY) | 880 | if (lu->tgt->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY) |
869 | ssleep(SBP2_INQUIRY_DELAY); | 881 | ssleep(SBP2_INQUIRY_DELAY); |
870 | 882 | ||
871 | memset(&eight_bytes_lun, 0, sizeof(eight_bytes_lun)); | ||
872 | eight_bytes_lun.scsi_lun[0] = (lu->lun >> 8) & 0xff; | ||
873 | eight_bytes_lun.scsi_lun[1] = lu->lun & 0xff; | ||
874 | shost = container_of((void *)tgt, struct Scsi_Host, hostdata[0]); | 883 | shost = container_of((void *)tgt, struct Scsi_Host, hostdata[0]); |
875 | 884 | sdev = __scsi_add_device(shost, 0, 0, sbp2_lun2int(lu->lun), lu); | |
876 | sdev = __scsi_add_device(shost, 0, 0, | ||
877 | scsilun_to_int(&eight_bytes_lun), lu); | ||
878 | /* | 885 | /* |
879 | * FIXME: We are unable to perform reconnects while in sbp2_login(). | 886 | * FIXME: We are unable to perform reconnects while in sbp2_login(). |
880 | * Therefore __scsi_add_device() will get into trouble if a bus reset | 887 | * Therefore __scsi_add_device() will get into trouble if a bus reset |
@@ -896,7 +903,8 @@ static void sbp2_login(struct work_struct *work) | |||
896 | } | 903 | } |
897 | 904 | ||
898 | /* No error during __scsi_add_device() */ | 905 | /* No error during __scsi_add_device() */ |
899 | lu->sdev = sdev; | 906 | lu->has_sdev = true; |
907 | scsi_device_put(sdev); | ||
900 | sbp2_allow_block(lu); | 908 | sbp2_allow_block(lu); |
901 | goto out; | 909 | goto out; |
902 | 910 | ||
@@ -934,11 +942,11 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) | |||
934 | return -ENOMEM; | 942 | return -ENOMEM; |
935 | } | 943 | } |
936 | 944 | ||
937 | lu->tgt = tgt; | 945 | lu->tgt = tgt; |
938 | lu->sdev = NULL; | 946 | lu->lun = lun_entry & 0xffff; |
939 | lu->lun = lun_entry & 0xffff; | 947 | lu->retries = 0; |
940 | lu->retries = 0; | 948 | lu->has_sdev = false; |
941 | lu->blocked = false; | 949 | lu->blocked = false; |
942 | ++tgt->dont_block; | 950 | ++tgt->dont_block; |
943 | INIT_LIST_HEAD(&lu->orb_list); | 951 | INIT_LIST_HEAD(&lu->orb_list); |
944 | INIT_DELAYED_WORK(&lu->work, sbp2_login); | 952 | INIT_DELAYED_WORK(&lu->work, sbp2_login); |