diff options
Diffstat (limited to 'drivers/scsi/hosts.c')
-rw-r--r-- | drivers/scsi/hosts.c | 88 |
1 files changed, 82 insertions, 6 deletions
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 5feb886c3392..6828ca305c2a 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c | |||
@@ -52,6 +52,82 @@ static struct class shost_class = { | |||
52 | }; | 52 | }; |
53 | 53 | ||
54 | /** | 54 | /** |
55 | * scsi_host_set_state - Take the given host through the host | ||
56 | * state model. | ||
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. | ||
62 | **/ | ||
63 | int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) | ||
64 | { | ||
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; | ||
114 | |||
115 | } | ||
116 | shost->shost_state = 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; | ||
127 | } | ||
128 | EXPORT_SYMBOL(scsi_host_set_state); | ||
129 | |||
130 | /** | ||
55 | * scsi_host_cancel - cancel outstanding IO to this host | 131 | * scsi_host_cancel - cancel outstanding IO to this host |
56 | * @shost: pointer to struct Scsi_Host | 132 | * @shost: pointer to struct Scsi_Host |
57 | * recovery: recovery requested to run. | 133 | * recovery: recovery requested to run. |
@@ -60,12 +136,11 @@ static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) | |||
60 | { | 136 | { |
61 | struct scsi_device *sdev; | 137 | struct scsi_device *sdev; |
62 | 138 | ||
63 | set_bit(SHOST_CANCEL, &shost->shost_state); | 139 | scsi_host_set_state(shost, SHOST_CANCEL); |
64 | shost_for_each_device(sdev, shost) { | 140 | shost_for_each_device(sdev, shost) { |
65 | scsi_device_cancel(sdev, recovery); | 141 | scsi_device_cancel(sdev, recovery); |
66 | } | 142 | } |
67 | wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY, | 143 | wait_event(shost->host_wait, (shost->shost_state != SHOST_RECOVERY)); |
68 | &shost->shost_state))); | ||
69 | } | 144 | } |
70 | 145 | ||
71 | /** | 146 | /** |
@@ -78,7 +153,7 @@ void scsi_remove_host(struct Scsi_Host *shost) | |||
78 | scsi_host_cancel(shost, 0); | 153 | scsi_host_cancel(shost, 0); |
79 | scsi_proc_host_rm(shost); | 154 | scsi_proc_host_rm(shost); |
80 | 155 | ||
81 | set_bit(SHOST_DEL, &shost->shost_state); | 156 | scsi_host_set_state(shost, SHOST_DEL); |
82 | 157 | ||
83 | transport_unregister_device(&shost->shost_gendev); | 158 | transport_unregister_device(&shost->shost_gendev); |
84 | class_device_unregister(&shost->shost_classdev); | 159 | class_device_unregister(&shost->shost_classdev); |
@@ -115,7 +190,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) | |||
115 | if (error) | 190 | if (error) |
116 | goto out; | 191 | goto out; |
117 | 192 | ||
118 | set_bit(SHOST_ADD, &shost->shost_state); | 193 | scsi_host_set_state(shost, SHOST_RUNNING); |
119 | get_device(shost->shost_gendev.parent); | 194 | get_device(shost->shost_gendev.parent); |
120 | 195 | ||
121 | error = class_device_add(&shost->shost_classdev); | 196 | error = class_device_add(&shost->shost_classdev); |
@@ -226,6 +301,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) | |||
226 | 301 | ||
227 | spin_lock_init(&shost->default_lock); | 302 | spin_lock_init(&shost->default_lock); |
228 | scsi_assign_lock(shost, &shost->default_lock); | 303 | scsi_assign_lock(shost, &shost->default_lock); |
304 | shost->shost_state = SHOST_CREATED; | ||
229 | INIT_LIST_HEAD(&shost->__devices); | 305 | INIT_LIST_HEAD(&shost->__devices); |
230 | INIT_LIST_HEAD(&shost->__targets); | 306 | INIT_LIST_HEAD(&shost->__targets); |
231 | INIT_LIST_HEAD(&shost->eh_cmd_q); | 307 | INIT_LIST_HEAD(&shost->eh_cmd_q); |
@@ -382,7 +458,7 @@ EXPORT_SYMBOL(scsi_host_lookup); | |||
382 | **/ | 458 | **/ |
383 | struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) | 459 | struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) |
384 | { | 460 | { |
385 | if (test_bit(SHOST_DEL, &shost->shost_state) || | 461 | if ((shost->shost_state == SHOST_DEL) || |
386 | !get_device(&shost->shost_gendev)) | 462 | !get_device(&shost->shost_gendev)) |
387 | return NULL; | 463 | return NULL; |
388 | return shost; | 464 | return shost; |