diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2011-08-27 09:33:34 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2011-09-16 16:23:56 -0400 |
commit | 6ff8147d075da2e1eb69fab2ee75104c59f573e0 (patch) | |
tree | 8153760829514f80842f5b36159513002b07e577 /drivers/firewire/sbp2.c | |
parent | f39aa30d7741f40ad964341e9243dbbd7f8ff057 (diff) |
firewire: sbp2: remove obsolete reference counting
Since commit 0278ccd9d53e07c4e699432b2fed9de6c56f506c "firewire: sbp2:
fix panic after rmmod with slow targets", the lifetime of an sbp2_target
instance does no longer extent past the return of sbp2_remove().
Therefore it is no longer necessary to call fw_unit_get/put() and
fw_device_get/put() in sbp2_probe/remove().
Furthermore, said commit also ensures that lu->work is not going to be
executed or requeued at a time when the sbp2_target is no longer in use.
Hence there is no need for sbp2_target reference counting for lu->work.
Other concurrent contexts:
- Processes which access the sysfs of the SCSI host device or of one
of its subdevices are safe because these interfaces are all removed
by scsi_remove_device/host() in sbp2_release_target().
- SBP-2 command block ORB transactions are finished when
scsi_remove_device() in sbp2_release_target() returns.
- SBP-2 management ORB transactions are finished when
cancel_delayed_work_sync(&lu->work) before sbp2_release_target()
returns.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/sbp2.c')
-rw-r--r-- | drivers/firewire/sbp2.c | 57 |
1 files changed, 17 insertions, 40 deletions
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 17cef864506..a2715b20ede 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c | |||
@@ -159,7 +159,6 @@ struct sbp2_logical_unit { | |||
159 | * and one struct Scsi_Host per sbp2_target. | 159 | * and one struct Scsi_Host per sbp2_target. |
160 | */ | 160 | */ |
161 | struct sbp2_target { | 161 | struct sbp2_target { |
162 | struct kref kref; | ||
163 | struct fw_unit *unit; | 162 | struct fw_unit *unit; |
164 | const char *bus_id; | 163 | const char *bus_id; |
165 | struct list_head lu_list; | 164 | struct list_head lu_list; |
@@ -772,9 +771,8 @@ static int sbp2_lun2int(u16 lun) | |||
772 | return scsilun_to_int(&eight_bytes_lun); | 771 | return scsilun_to_int(&eight_bytes_lun); |
773 | } | 772 | } |
774 | 773 | ||
775 | static void sbp2_release_target(struct kref *kref) | 774 | static void sbp2_release_target(struct sbp2_target *tgt) |
776 | { | 775 | { |
777 | struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref); | ||
778 | struct sbp2_logical_unit *lu, *next; | 776 | struct sbp2_logical_unit *lu, *next; |
779 | struct Scsi_Host *shost = | 777 | struct Scsi_Host *shost = |
780 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); | 778 | container_of((void *)tgt, struct Scsi_Host, hostdata[0]); |
@@ -811,30 +809,12 @@ static void sbp2_release_target(struct kref *kref) | |||
811 | scsi_remove_host(shost); | 809 | scsi_remove_host(shost); |
812 | fw_notify("released %s, target %d:0:0\n", tgt->bus_id, shost->host_no); | 810 | fw_notify("released %s, target %d:0:0\n", tgt->bus_id, shost->host_no); |
813 | 811 | ||
814 | fw_unit_put(tgt->unit); | ||
815 | scsi_host_put(shost); | 812 | scsi_host_put(shost); |
816 | fw_device_put(device); | ||
817 | } | ||
818 | |||
819 | static void sbp2_target_get(struct sbp2_target *tgt) | ||
820 | { | ||
821 | kref_get(&tgt->kref); | ||
822 | } | ||
823 | |||
824 | static void sbp2_target_put(struct sbp2_target *tgt) | ||
825 | { | ||
826 | kref_put(&tgt->kref, sbp2_release_target); | ||
827 | } | 813 | } |
828 | 814 | ||
829 | /* | ||
830 | * Always get the target's kref when scheduling work on one its units. | ||
831 | * Each workqueue job is responsible to call sbp2_target_put() upon return. | ||
832 | */ | ||
833 | static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay) | 815 | static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay) |
834 | { | 816 | { |
835 | sbp2_target_get(lu->tgt); | 817 | queue_delayed_work(fw_workqueue, &lu->work, delay); |
836 | if (!queue_delayed_work(fw_workqueue, &lu->work, delay)) | ||
837 | sbp2_target_put(lu->tgt); | ||
838 | } | 818 | } |
839 | 819 | ||
840 | /* | 820 | /* |
@@ -877,7 +857,7 @@ static void sbp2_login(struct work_struct *work) | |||
877 | int generation, node_id, local_node_id; | 857 | int generation, node_id, local_node_id; |
878 | 858 | ||
879 | if (fw_device_is_shutdown(device)) | 859 | if (fw_device_is_shutdown(device)) |
880 | goto out; | 860 | return; |
881 | 861 | ||
882 | generation = device->generation; | 862 | generation = device->generation; |
883 | smp_rmb(); /* node IDs must not be older than generation */ | 863 | smp_rmb(); /* node IDs must not be older than generation */ |
@@ -899,7 +879,7 @@ static void sbp2_login(struct work_struct *work) | |||
899 | /* Let any waiting I/O fail from now on. */ | 879 | /* Let any waiting I/O fail from now on. */ |
900 | sbp2_unblock(lu->tgt); | 880 | sbp2_unblock(lu->tgt); |
901 | } | 881 | } |
902 | goto out; | 882 | return; |
903 | } | 883 | } |
904 | 884 | ||
905 | tgt->node_id = node_id; | 885 | tgt->node_id = node_id; |
@@ -925,7 +905,8 @@ static void sbp2_login(struct work_struct *work) | |||
925 | if (lu->has_sdev) { | 905 | if (lu->has_sdev) { |
926 | sbp2_cancel_orbs(lu); | 906 | sbp2_cancel_orbs(lu); |
927 | sbp2_conditionally_unblock(lu); | 907 | sbp2_conditionally_unblock(lu); |
928 | goto out; | 908 | |
909 | return; | ||
929 | } | 910 | } |
930 | 911 | ||
931 | if (lu->tgt->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY) | 912 | if (lu->tgt->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY) |
@@ -957,7 +938,8 @@ static void sbp2_login(struct work_struct *work) | |||
957 | lu->has_sdev = true; | 938 | lu->has_sdev = true; |
958 | scsi_device_put(sdev); | 939 | scsi_device_put(sdev); |
959 | sbp2_allow_block(lu); | 940 | sbp2_allow_block(lu); |
960 | goto out; | 941 | |
942 | return; | ||
961 | 943 | ||
962 | out_logout_login: | 944 | out_logout_login: |
963 | smp_rmb(); /* generation may have changed */ | 945 | smp_rmb(); /* generation may have changed */ |
@@ -971,8 +953,6 @@ static void sbp2_login(struct work_struct *work) | |||
971 | * lu->work already. Reset the work from reconnect to login. | 953 | * lu->work already. Reset the work from reconnect to login. |
972 | */ | 954 | */ |
973 | PREPARE_DELAYED_WORK(&lu->work, sbp2_login); | 955 | PREPARE_DELAYED_WORK(&lu->work, sbp2_login); |
974 | out: | ||
975 | sbp2_target_put(tgt); | ||
976 | } | 956 | } |
977 | 957 | ||
978 | static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) | 958 | static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) |
@@ -1141,7 +1121,6 @@ static int sbp2_probe(struct device *dev) | |||
1141 | tgt = (struct sbp2_target *)shost->hostdata; | 1121 | tgt = (struct sbp2_target *)shost->hostdata; |
1142 | dev_set_drvdata(&unit->device, tgt); | 1122 | dev_set_drvdata(&unit->device, tgt); |
1143 | tgt->unit = unit; | 1123 | tgt->unit = unit; |
1144 | kref_init(&tgt->kref); | ||
1145 | INIT_LIST_HEAD(&tgt->lu_list); | 1124 | INIT_LIST_HEAD(&tgt->lu_list); |
1146 | tgt->bus_id = dev_name(&unit->device); | 1125 | tgt->bus_id = dev_name(&unit->device); |
1147 | tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; | 1126 | tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; |
@@ -1154,9 +1133,6 @@ static int sbp2_probe(struct device *dev) | |||
1154 | if (scsi_add_host(shost, &unit->device) < 0) | 1133 | if (scsi_add_host(shost, &unit->device) < 0) |
1155 | goto fail_shost_put; | 1134 | goto fail_shost_put; |
1156 | 1135 | ||
1157 | fw_device_get(device); | ||
1158 | fw_unit_get(unit); | ||
1159 | |||
1160 | /* implicit directory ID */ | 1136 | /* implicit directory ID */ |
1161 | tgt->directory_id = ((unit->directory - device->config_rom) * 4 | 1137 | tgt->directory_id = ((unit->directory - device->config_rom) * 4 |
1162 | + CSR_CONFIG_ROM) & 0xffffff; | 1138 | + CSR_CONFIG_ROM) & 0xffffff; |
@@ -1166,7 +1142,7 @@ static int sbp2_probe(struct device *dev) | |||
1166 | 1142 | ||
1167 | if (sbp2_scan_unit_dir(tgt, unit->directory, &model, | 1143 | if (sbp2_scan_unit_dir(tgt, unit->directory, &model, |
1168 | &firmware_revision) < 0) | 1144 | &firmware_revision) < 0) |
1169 | goto fail_tgt_put; | 1145 | goto fail_release_target; |
1170 | 1146 | ||
1171 | sbp2_clamp_management_orb_timeout(tgt); | 1147 | sbp2_clamp_management_orb_timeout(tgt); |
1172 | sbp2_init_workarounds(tgt, model, firmware_revision); | 1148 | sbp2_init_workarounds(tgt, model, firmware_revision); |
@@ -1183,10 +1159,11 @@ static int sbp2_probe(struct device *dev) | |||
1183 | /* Do the login in a workqueue so we can easily reschedule retries. */ | 1159 | /* Do the login in a workqueue so we can easily reschedule retries. */ |
1184 | list_for_each_entry(lu, &tgt->lu_list, link) | 1160 | list_for_each_entry(lu, &tgt->lu_list, link) |
1185 | sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); | 1161 | sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); |
1162 | |||
1186 | return 0; | 1163 | return 0; |
1187 | 1164 | ||
1188 | fail_tgt_put: | 1165 | fail_release_target: |
1189 | sbp2_target_put(tgt); | 1166 | sbp2_release_target(tgt); |
1190 | return -ENOMEM; | 1167 | return -ENOMEM; |
1191 | 1168 | ||
1192 | fail_shost_put: | 1169 | fail_shost_put: |
@@ -1203,7 +1180,8 @@ static int sbp2_remove(struct device *dev) | |||
1203 | list_for_each_entry(lu, &tgt->lu_list, link) | 1180 | list_for_each_entry(lu, &tgt->lu_list, link) |
1204 | cancel_delayed_work_sync(&lu->work); | 1181 | cancel_delayed_work_sync(&lu->work); |
1205 | 1182 | ||
1206 | sbp2_target_put(tgt); | 1183 | sbp2_release_target(tgt); |
1184 | |||
1207 | return 0; | 1185 | return 0; |
1208 | } | 1186 | } |
1209 | 1187 | ||
@@ -1216,7 +1194,7 @@ static void sbp2_reconnect(struct work_struct *work) | |||
1216 | int generation, node_id, local_node_id; | 1194 | int generation, node_id, local_node_id; |
1217 | 1195 | ||
1218 | if (fw_device_is_shutdown(device)) | 1196 | if (fw_device_is_shutdown(device)) |
1219 | goto out; | 1197 | return; |
1220 | 1198 | ||
1221 | generation = device->generation; | 1199 | generation = device->generation; |
1222 | smp_rmb(); /* node IDs must not be older than generation */ | 1200 | smp_rmb(); /* node IDs must not be older than generation */ |
@@ -1241,7 +1219,8 @@ static void sbp2_reconnect(struct work_struct *work) | |||
1241 | PREPARE_DELAYED_WORK(&lu->work, sbp2_login); | 1219 | PREPARE_DELAYED_WORK(&lu->work, sbp2_login); |
1242 | } | 1220 | } |
1243 | sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); | 1221 | sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); |
1244 | goto out; | 1222 | |
1223 | return; | ||
1245 | } | 1224 | } |
1246 | 1225 | ||
1247 | tgt->node_id = node_id; | 1226 | tgt->node_id = node_id; |
@@ -1255,8 +1234,6 @@ static void sbp2_reconnect(struct work_struct *work) | |||
1255 | sbp2_agent_reset(lu); | 1234 | sbp2_agent_reset(lu); |
1256 | sbp2_cancel_orbs(lu); | 1235 | sbp2_cancel_orbs(lu); |
1257 | sbp2_conditionally_unblock(lu); | 1236 | sbp2_conditionally_unblock(lu); |
1258 | out: | ||
1259 | sbp2_target_put(tgt); | ||
1260 | } | 1237 | } |
1261 | 1238 | ||
1262 | static void sbp2_update(struct fw_unit *unit) | 1239 | static void sbp2_update(struct fw_unit *unit) |