aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@suse.de>2009-11-19 17:48:29 -0500
committerJames Bottomley <James.Bottomley@suse.de>2009-11-26 10:43:39 -0500
commit860dc73608a091e0b325218acc2701709d5f221a (patch)
tree2527b226e1991c459ac02de4a6ba5c98a4639add
parent3bf3583b6a49c318f7ed350862d7a217b500e71c (diff)
[SCSI] fix async scan add/remove race resulting in an oops
Async scanning introduced a very wide window where the SCSI device is up and running but has not yet been added to sysfs. We delay the adding until all scans have completed to retain the same ordering as sync scanning. This delay in visibility causes an oops if a device is removed before we make it visible because the SCSI removal routines have an inbuilt assumption that if a device is in SDEV_RUNNING state, it must be visible (which is not necessarily true in the async scanning case). Fix this by introducing an additional is_visible flag which we can use to condition the tear down so we do the right thing for running but not yet made visible. Reported-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
-rw-r--r--drivers/scsi/scsi_scan.c18
-rw-r--r--drivers/scsi/scsi_sysfs.c63
-rw-r--r--include/scsi/scsi_device.h1
3 files changed, 32 insertions, 50 deletions
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 0547a7f44d42..47291bcff0d5 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -952,16 +952,6 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
952 return SCSI_SCAN_LUN_PRESENT; 952 return SCSI_SCAN_LUN_PRESENT;
953} 953}
954 954
955static inline void scsi_destroy_sdev(struct scsi_device *sdev)
956{
957 scsi_device_set_state(sdev, SDEV_DEL);
958 if (sdev->host->hostt->slave_destroy)
959 sdev->host->hostt->slave_destroy(sdev);
960 transport_destroy_device(&sdev->sdev_gendev);
961 put_device(&sdev->sdev_dev);
962 put_device(&sdev->sdev_gendev);
963}
964
965#ifdef CONFIG_SCSI_LOGGING 955#ifdef CONFIG_SCSI_LOGGING
966/** 956/**
967 * scsi_inq_str - print INQUIRY data from min to max index, strip trailing whitespace 957 * scsi_inq_str - print INQUIRY data from min to max index, strip trailing whitespace
@@ -1139,7 +1129,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
1139 } 1129 }
1140 } 1130 }
1141 } else 1131 } else
1142 scsi_destroy_sdev(sdev); 1132 __scsi_remove_device(sdev);
1143 out: 1133 out:
1144 return res; 1134 return res;
1145} 1135}
@@ -1500,7 +1490,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
1500 /* 1490 /*
1501 * the sdev we used didn't appear in the report luns scan 1491 * the sdev we used didn't appear in the report luns scan
1502 */ 1492 */
1503 scsi_destroy_sdev(sdev); 1493 __scsi_remove_device(sdev);
1504 return ret; 1494 return ret;
1505} 1495}
1506 1496
@@ -1710,7 +1700,7 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost)
1710 shost_for_each_device(sdev, shost) { 1700 shost_for_each_device(sdev, shost) {
1711 if (!scsi_host_scan_allowed(shost) || 1701 if (!scsi_host_scan_allowed(shost) ||
1712 scsi_sysfs_add_sdev(sdev) != 0) 1702 scsi_sysfs_add_sdev(sdev) != 0)
1713 scsi_destroy_sdev(sdev); 1703 __scsi_remove_device(sdev);
1714 } 1704 }
1715} 1705}
1716 1706
@@ -1943,7 +1933,7 @@ void scsi_free_host_dev(struct scsi_device *sdev)
1943{ 1933{
1944 BUG_ON(sdev->id != sdev->host->this_id); 1934 BUG_ON(sdev->id != sdev->host->this_id);
1945 1935
1946 scsi_destroy_sdev(sdev); 1936 __scsi_remove_device(sdev);
1947} 1937}
1948EXPORT_SYMBOL(scsi_free_host_dev); 1938EXPORT_SYMBOL(scsi_free_host_dev);
1949 1939
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 5c7eb63a19d1..392d8db33905 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -854,82 +854,73 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
854 transport_configure_device(&starget->dev); 854 transport_configure_device(&starget->dev);
855 error = device_add(&sdev->sdev_gendev); 855 error = device_add(&sdev->sdev_gendev);
856 if (error) { 856 if (error) {
857 put_device(sdev->sdev_gendev.parent);
858 printk(KERN_INFO "error 1\n"); 857 printk(KERN_INFO "error 1\n");
859 return error; 858 goto out_remove;
860 } 859 }
861 error = device_add(&sdev->sdev_dev); 860 error = device_add(&sdev->sdev_dev);
862 if (error) { 861 if (error) {
863 printk(KERN_INFO "error 2\n"); 862 printk(KERN_INFO "error 2\n");
864 goto clean_device; 863 device_del(&sdev->sdev_gendev);
864 goto out_remove;
865 } 865 }
866 transport_add_device(&sdev->sdev_gendev);
867 sdev->is_visible = 1;
866 868
867 /* create queue files, which may be writable, depending on the host */ 869 /* create queue files, which may be writable, depending on the host */
868 if (sdev->host->hostt->change_queue_depth) 870 if (sdev->host->hostt->change_queue_depth)
869 error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_depth_rw); 871 error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_depth_rw);
870 else 872 else
871 error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth); 873 error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth);
872 if (error) { 874 if (error)
873 __scsi_remove_device(sdev); 875 goto out_remove;
874 goto out; 876
875 }
876 if (sdev->host->hostt->change_queue_type) 877 if (sdev->host->hostt->change_queue_type)
877 error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_type_rw); 878 error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_type_rw);
878 else 879 else
879 error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_type); 880 error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_type);
880 if (error) { 881 if (error)
881 __scsi_remove_device(sdev); 882 goto out_remove;
882 goto out;
883 }
884 883
885 error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL); 884 error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL);
886 885
887 if (error) 886 if (error)
887 /* we're treating error on bsg register as non-fatal,
888 * so pretend nothing went wrong */
888 sdev_printk(KERN_INFO, sdev, 889 sdev_printk(KERN_INFO, sdev,
889 "Failed to register bsg queue, errno=%d\n", error); 890 "Failed to register bsg queue, errno=%d\n", error);
890 891
891 /* we're treating error on bsg register as non-fatal, so pretend
892 * nothing went wrong */
893 error = 0;
894
895 /* add additional host specific attributes */ 892 /* add additional host specific attributes */
896 if (sdev->host->hostt->sdev_attrs) { 893 if (sdev->host->hostt->sdev_attrs) {
897 for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) { 894 for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) {
898 error = device_create_file(&sdev->sdev_gendev, 895 error = device_create_file(&sdev->sdev_gendev,
899 sdev->host->hostt->sdev_attrs[i]); 896 sdev->host->hostt->sdev_attrs[i]);
900 if (error) { 897 if (error)
901 __scsi_remove_device(sdev); 898 goto out_remove;
902 goto out;
903 }
904 } 899 }
905 } 900 }
906 901
907 transport_add_device(&sdev->sdev_gendev); 902 return 0;
908 out:
909 return error;
910
911 clean_device:
912 scsi_device_set_state(sdev, SDEV_CANCEL);
913
914 device_del(&sdev->sdev_gendev);
915 transport_destroy_device(&sdev->sdev_gendev);
916 put_device(&sdev->sdev_dev);
917 put_device(&sdev->sdev_gendev);
918 903
904 out_remove:
905 __scsi_remove_device(sdev);
919 return error; 906 return error;
907
920} 908}
921 909
922void __scsi_remove_device(struct scsi_device *sdev) 910void __scsi_remove_device(struct scsi_device *sdev)
923{ 911{
924 struct device *dev = &sdev->sdev_gendev; 912 struct device *dev = &sdev->sdev_gendev;
925 913
926 if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) 914 if (sdev->is_visible) {
927 return; 915 if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
916 return;
928 917
929 bsg_unregister_queue(sdev->request_queue); 918 bsg_unregister_queue(sdev->request_queue);
930 device_unregister(&sdev->sdev_dev); 919 device_unregister(&sdev->sdev_dev);
931 transport_remove_device(dev); 920 transport_remove_device(dev);
932 device_del(dev); 921 device_del(dev);
922 } else
923 put_device(&sdev->sdev_dev);
933 scsi_device_set_state(sdev, SDEV_DEL); 924 scsi_device_set_state(sdev, SDEV_DEL);
934 if (sdev->host->hostt->slave_destroy) 925 if (sdev->host->hostt->slave_destroy)
935 sdev->host->hostt->slave_destroy(sdev); 926 sdev->host->hostt->slave_destroy(sdev);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 9af48cbf0036..f097ae340bc1 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -145,6 +145,7 @@ struct scsi_device {
145 unsigned retry_hwerror:1; /* Retry HARDWARE_ERROR */ 145 unsigned retry_hwerror:1; /* Retry HARDWARE_ERROR */
146 unsigned last_sector_bug:1; /* do not use multisector accesses on 146 unsigned last_sector_bug:1; /* do not use multisector accesses on
147 SD_LAST_BUGGY_SECTORS */ 147 SD_LAST_BUGGY_SECTORS */
148 unsigned is_visible:1; /* is the device visible in sysfs */
148 149
149 DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ 150 DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
150 struct list_head event_list; /* asserted events */ 151 struct list_head event_list; /* asserted events */