diff options
Diffstat (limited to 'drivers/scsi/hosts.c')
-rw-r--r-- | drivers/scsi/hosts.c | 90 |
1 files changed, 76 insertions, 14 deletions
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 5feb886c3392..8640ad1c17e2 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c | |||
@@ -52,21 +52,80 @@ static struct class shost_class = { | |||
52 | }; | 52 | }; |
53 | 53 | ||
54 | /** | 54 | /** |
55 | * scsi_host_cancel - cancel outstanding IO to this host | 55 | * scsi_host_set_state - Take the given host through the host |
56 | * @shost: pointer to struct Scsi_Host | 56 | * state model. |
57 | * recovery: recovery requested to run. | 57 | * @shost: scsi host to change the state of. |
58 | * @state: state to change to. | ||
59 | * | ||
60 | * Returns zero if unsuccessful or an error if the requested | ||
61 | * transition is illegal. | ||
58 | **/ | 62 | **/ |
59 | static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) | 63 | int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) |
60 | { | 64 | { |
61 | struct scsi_device *sdev; | 65 | enum scsi_host_state oldstate = shost->shost_state; |
66 | |||
67 | if (state == oldstate) | ||
68 | return 0; | ||
69 | |||
70 | switch (state) { | ||
71 | case SHOST_CREATED: | ||
72 | /* There are no legal states that come back to | ||
73 | * created. This is the manually initialised start | ||
74 | * state */ | ||
75 | goto illegal; | ||
76 | |||
77 | case SHOST_RUNNING: | ||
78 | switch (oldstate) { | ||
79 | case SHOST_CREATED: | ||
80 | case SHOST_RECOVERY: | ||
81 | break; | ||
82 | default: | ||
83 | goto illegal; | ||
84 | } | ||
85 | break; | ||
86 | |||
87 | case SHOST_RECOVERY: | ||
88 | switch (oldstate) { | ||
89 | case SHOST_RUNNING: | ||
90 | break; | ||
91 | default: | ||
92 | goto illegal; | ||
93 | } | ||
94 | break; | ||
95 | |||
96 | case SHOST_CANCEL: | ||
97 | switch (oldstate) { | ||
98 | case SHOST_CREATED: | ||
99 | case SHOST_RUNNING: | ||
100 | break; | ||
101 | default: | ||
102 | goto illegal; | ||
103 | } | ||
104 | break; | ||
105 | |||
106 | case SHOST_DEL: | ||
107 | switch (oldstate) { | ||
108 | case SHOST_CANCEL: | ||
109 | break; | ||
110 | default: | ||
111 | goto illegal; | ||
112 | } | ||
113 | break; | ||
62 | 114 | ||
63 | set_bit(SHOST_CANCEL, &shost->shost_state); | ||
64 | shost_for_each_device(sdev, shost) { | ||
65 | scsi_device_cancel(sdev, recovery); | ||
66 | } | 115 | } |
67 | wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY, | 116 | shost->shost_state = state; |
68 | &shost->shost_state))); | 117 | return 0; |
118 | |||
119 | illegal: | ||
120 | SCSI_LOG_ERROR_RECOVERY(1, | ||
121 | dev_printk(KERN_ERR, &shost->shost_gendev, | ||
122 | "Illegal host state transition" | ||
123 | "%s->%s\n", | ||
124 | scsi_host_state_name(oldstate), | ||
125 | scsi_host_state_name(state))); | ||
126 | return -EINVAL; | ||
69 | } | 127 | } |
128 | EXPORT_SYMBOL(scsi_host_set_state); | ||
70 | 129 | ||
71 | /** | 130 | /** |
72 | * scsi_remove_host - remove a scsi host | 131 | * scsi_remove_host - remove a scsi host |
@@ -74,11 +133,13 @@ static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) | |||
74 | **/ | 133 | **/ |
75 | void scsi_remove_host(struct Scsi_Host *shost) | 134 | void scsi_remove_host(struct Scsi_Host *shost) |
76 | { | 135 | { |
136 | down(&shost->scan_mutex); | ||
137 | scsi_host_set_state(shost, SHOST_CANCEL); | ||
138 | up(&shost->scan_mutex); | ||
77 | scsi_forget_host(shost); | 139 | scsi_forget_host(shost); |
78 | scsi_host_cancel(shost, 0); | ||
79 | scsi_proc_host_rm(shost); | 140 | scsi_proc_host_rm(shost); |
80 | 141 | ||
81 | set_bit(SHOST_DEL, &shost->shost_state); | 142 | scsi_host_set_state(shost, SHOST_DEL); |
82 | 143 | ||
83 | transport_unregister_device(&shost->shost_gendev); | 144 | transport_unregister_device(&shost->shost_gendev); |
84 | class_device_unregister(&shost->shost_classdev); | 145 | class_device_unregister(&shost->shost_classdev); |
@@ -115,7 +176,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) | |||
115 | if (error) | 176 | if (error) |
116 | goto out; | 177 | goto out; |
117 | 178 | ||
118 | set_bit(SHOST_ADD, &shost->shost_state); | 179 | scsi_host_set_state(shost, SHOST_RUNNING); |
119 | get_device(shost->shost_gendev.parent); | 180 | get_device(shost->shost_gendev.parent); |
120 | 181 | ||
121 | error = class_device_add(&shost->shost_classdev); | 182 | error = class_device_add(&shost->shost_classdev); |
@@ -226,6 +287,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) | |||
226 | 287 | ||
227 | spin_lock_init(&shost->default_lock); | 288 | spin_lock_init(&shost->default_lock); |
228 | scsi_assign_lock(shost, &shost->default_lock); | 289 | scsi_assign_lock(shost, &shost->default_lock); |
290 | shost->shost_state = SHOST_CREATED; | ||
229 | INIT_LIST_HEAD(&shost->__devices); | 291 | INIT_LIST_HEAD(&shost->__devices); |
230 | INIT_LIST_HEAD(&shost->__targets); | 292 | INIT_LIST_HEAD(&shost->__targets); |
231 | INIT_LIST_HEAD(&shost->eh_cmd_q); | 293 | INIT_LIST_HEAD(&shost->eh_cmd_q); |
@@ -382,7 +444,7 @@ EXPORT_SYMBOL(scsi_host_lookup); | |||
382 | **/ | 444 | **/ |
383 | struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) | 445 | struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) |
384 | { | 446 | { |
385 | if (test_bit(SHOST_DEL, &shost->shost_state) || | 447 | if ((shost->shost_state == SHOST_DEL) || |
386 | !get_device(&shost->shost_gendev)) | 448 | !get_device(&shost->shost_gendev)) |
387 | return NULL; | 449 | return NULL; |
388 | return shost; | 450 | return shost; |