diff options
-rw-r--r-- | drivers/scsi/hosts.c | 88 | ||||
-rw-r--r-- | drivers/scsi/scsi.c | 2 | ||||
-rw-r--r-- | drivers/scsi/scsi_error.c | 7 | ||||
-rw-r--r-- | drivers/scsi/scsi_ioctl.c | 3 | ||||
-rw-r--r-- | drivers/scsi/scsi_lib.c | 4 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 62 | ||||
-rw-r--r-- | drivers/scsi/sg.c | 3 | ||||
-rw-r--r-- | include/scsi/scsi_host.h | 14 |
8 files changed, 162 insertions, 21 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; |
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index d14523d7e449..fb85b3ced7b5 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c | |||
@@ -627,7 +627,7 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) | |||
627 | spin_lock_irqsave(host->host_lock, flags); | 627 | spin_lock_irqsave(host->host_lock, flags); |
628 | scsi_cmd_get_serial(host, cmd); | 628 | scsi_cmd_get_serial(host, cmd); |
629 | 629 | ||
630 | if (unlikely(test_bit(SHOST_CANCEL, &host->shost_state))) { | 630 | if (unlikely(host->shost_state == SHOST_CANCEL)) { |
631 | cmd->result = (DID_NO_CONNECT << 16); | 631 | cmd->result = (DID_NO_CONNECT << 16); |
632 | scsi_done(cmd); | 632 | scsi_done(cmd); |
633 | } else { | 633 | } else { |
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 0fc8b48f052b..e9c451ba71fc 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -75,7 +75,7 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) | |||
75 | 75 | ||
76 | scmd->eh_eflags |= eh_flag; | 76 | scmd->eh_eflags |= eh_flag; |
77 | list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); | 77 | list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); |
78 | set_bit(SHOST_RECOVERY, &shost->shost_state); | 78 | scsi_host_set_state(shost, SHOST_RECOVERY); |
79 | shost->host_failed++; | 79 | shost->host_failed++; |
80 | scsi_eh_wakeup(shost); | 80 | scsi_eh_wakeup(shost); |
81 | spin_unlock_irqrestore(shost->host_lock, flags); | 81 | spin_unlock_irqrestore(shost->host_lock, flags); |
@@ -197,7 +197,8 @@ int scsi_block_when_processing_errors(struct scsi_device *sdev) | |||
197 | { | 197 | { |
198 | int online; | 198 | int online; |
199 | 199 | ||
200 | wait_event(sdev->host->host_wait, (!test_bit(SHOST_RECOVERY, &sdev->host->shost_state))); | 200 | wait_event(sdev->host->host_wait, (sdev->host->shost_state != |
201 | SHOST_RECOVERY)); | ||
201 | 202 | ||
202 | online = scsi_device_online(sdev); | 203 | online = scsi_device_online(sdev); |
203 | 204 | ||
@@ -1458,7 +1459,7 @@ static void scsi_restart_operations(struct Scsi_Host *shost) | |||
1458 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n", | 1459 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n", |
1459 | __FUNCTION__)); | 1460 | __FUNCTION__)); |
1460 | 1461 | ||
1461 | clear_bit(SHOST_RECOVERY, &shost->shost_state); | 1462 | scsi_host_set_state(shost, SHOST_RUNNING); |
1462 | 1463 | ||
1463 | wake_up(&shost->host_wait); | 1464 | wake_up(&shost->host_wait); |
1464 | 1465 | ||
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 7a6b530115ac..f5bf5c07be91 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c | |||
@@ -475,8 +475,7 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, | |||
475 | * error processing, as long as the device was opened | 475 | * error processing, as long as the device was opened |
476 | * non-blocking */ | 476 | * non-blocking */ |
477 | if (filp && filp->f_flags & O_NONBLOCK) { | 477 | if (filp && filp->f_flags & O_NONBLOCK) { |
478 | if (test_bit(SHOST_RECOVERY, | 478 | if (sdev->host->shost_state == SHOST_RECOVERY) |
479 | &sdev->host->shost_state)) | ||
480 | return -ENODEV; | 479 | return -ENODEV; |
481 | } else if (!scsi_block_when_processing_errors(sdev)) | 480 | } else if (!scsi_block_when_processing_errors(sdev)) |
482 | return -ENODEV; | 481 | return -ENODEV; |
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 7a91ca3d32a6..060010bccabc 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c | |||
@@ -348,7 +348,7 @@ void scsi_device_unbusy(struct scsi_device *sdev) | |||
348 | 348 | ||
349 | spin_lock_irqsave(shost->host_lock, flags); | 349 | spin_lock_irqsave(shost->host_lock, flags); |
350 | shost->host_busy--; | 350 | shost->host_busy--; |
351 | if (unlikely(test_bit(SHOST_RECOVERY, &shost->shost_state) && | 351 | if (unlikely((shost->shost_state == SHOST_RECOVERY) && |
352 | shost->host_failed)) | 352 | shost->host_failed)) |
353 | scsi_eh_wakeup(shost); | 353 | scsi_eh_wakeup(shost); |
354 | spin_unlock(shost->host_lock); | 354 | spin_unlock(shost->host_lock); |
@@ -1207,7 +1207,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q, | |||
1207 | struct Scsi_Host *shost, | 1207 | struct Scsi_Host *shost, |
1208 | struct scsi_device *sdev) | 1208 | struct scsi_device *sdev) |
1209 | { | 1209 | { |
1210 | if (test_bit(SHOST_RECOVERY, &shost->shost_state)) | 1210 | if (shost->shost_state == SHOST_RECOVERY) |
1211 | return 0; | 1211 | return 0; |
1212 | if (shost->host_busy == 0 && shost->host_blocked) { | 1212 | if (shost->host_busy == 0 && shost->host_blocked) { |
1213 | /* | 1213 | /* |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index beed7fbe1cbe..dae59d1da07a 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -48,6 +48,30 @@ const char *scsi_device_state_name(enum scsi_device_state state) | |||
48 | return name; | 48 | return name; |
49 | } | 49 | } |
50 | 50 | ||
51 | static struct { | ||
52 | enum scsi_host_state value; | ||
53 | char *name; | ||
54 | } shost_states[] = { | ||
55 | { SHOST_CREATED, "created" }, | ||
56 | { SHOST_RUNNING, "running" }, | ||
57 | { SHOST_CANCEL, "cancel" }, | ||
58 | { SHOST_DEL, "deleted" }, | ||
59 | { SHOST_RECOVERY, "recovery" }, | ||
60 | }; | ||
61 | const char *scsi_host_state_name(enum scsi_host_state state) | ||
62 | { | ||
63 | int i; | ||
64 | char *name = NULL; | ||
65 | |||
66 | for (i = 0; i < sizeof(shost_states)/sizeof(shost_states[0]); i++) { | ||
67 | if (shost_states[i].value == state) { | ||
68 | name = shost_states[i].name; | ||
69 | break; | ||
70 | } | ||
71 | } | ||
72 | return name; | ||
73 | } | ||
74 | |||
51 | static int check_set(unsigned int *val, char *src) | 75 | static int check_set(unsigned int *val, char *src) |
52 | { | 76 | { |
53 | char *last; | 77 | char *last; |
@@ -124,6 +148,43 @@ static ssize_t store_scan(struct class_device *class_dev, const char *buf, | |||
124 | }; | 148 | }; |
125 | static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); | 149 | static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); |
126 | 150 | ||
151 | static ssize_t | ||
152 | store_shost_state(struct class_device *class_dev, const char *buf, size_t count) | ||
153 | { | ||
154 | int i; | ||
155 | struct Scsi_Host *shost = class_to_shost(class_dev); | ||
156 | enum scsi_host_state state = 0; | ||
157 | |||
158 | for (i = 0; i < sizeof(shost_states)/sizeof(shost_states[0]); i++) { | ||
159 | const int len = strlen(shost_states[i].name); | ||
160 | if (strncmp(shost_states[i].name, buf, len) == 0 && | ||
161 | buf[len] == '\n') { | ||
162 | state = shost_states[i].value; | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | if (!state) | ||
167 | return -EINVAL; | ||
168 | |||
169 | if (scsi_host_set_state(shost, state)) | ||
170 | return -EINVAL; | ||
171 | return count; | ||
172 | } | ||
173 | |||
174 | static ssize_t | ||
175 | show_shost_state(struct class_device *class_dev, char *buf) | ||
176 | { | ||
177 | struct Scsi_Host *shost = class_to_shost(class_dev); | ||
178 | const char *name = scsi_host_state_name(shost->shost_state); | ||
179 | |||
180 | if (!name) | ||
181 | return -EINVAL; | ||
182 | |||
183 | return snprintf(buf, 20, "%s\n", name); | ||
184 | } | ||
185 | |||
186 | static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_shost_state, store_shost_state); | ||
187 | |||
127 | shost_rd_attr(unique_id, "%u\n"); | 188 | shost_rd_attr(unique_id, "%u\n"); |
128 | shost_rd_attr(host_busy, "%hu\n"); | 189 | shost_rd_attr(host_busy, "%hu\n"); |
129 | shost_rd_attr(cmd_per_lun, "%hd\n"); | 190 | shost_rd_attr(cmd_per_lun, "%hd\n"); |
@@ -139,6 +200,7 @@ static struct class_device_attribute *scsi_sysfs_shost_attrs[] = { | |||
139 | &class_device_attr_unchecked_isa_dma, | 200 | &class_device_attr_unchecked_isa_dma, |
140 | &class_device_attr_proc_name, | 201 | &class_device_attr_proc_name, |
141 | &class_device_attr_scan, | 202 | &class_device_attr_scan, |
203 | &class_device_attr_state, | ||
142 | NULL | 204 | NULL |
143 | }; | 205 | }; |
144 | 206 | ||
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 51292f269ce5..14fb179b3842 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c | |||
@@ -1027,8 +1027,7 @@ sg_ioctl(struct inode *inode, struct file *filp, | |||
1027 | if (sdp->detached) | 1027 | if (sdp->detached) |
1028 | return -ENODEV; | 1028 | return -ENODEV; |
1029 | if (filp->f_flags & O_NONBLOCK) { | 1029 | if (filp->f_flags & O_NONBLOCK) { |
1030 | if (test_bit(SHOST_RECOVERY, | 1030 | if (sdp->device->host->shost_state == SHOST_RECOVERY) |
1031 | &sdp->device->host->shost_state)) | ||
1032 | return -EBUSY; | 1031 | return -EBUSY; |
1033 | } else if (!scsi_block_when_processing_errors(sdp->device)) | 1032 | } else if (!scsi_block_when_processing_errors(sdp->device)) |
1034 | return -EBUSY; | 1033 | return -EBUSY; |
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 81d5234f6771..0b1e275b2699 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h | |||
@@ -429,12 +429,15 @@ struct scsi_host_template { | |||
429 | }; | 429 | }; |
430 | 430 | ||
431 | /* | 431 | /* |
432 | * shost states | 432 | * shost state: If you alter this, you also need to alter scsi_sysfs.c |
433 | * (for the ascii descriptions) and the state model enforcer: | ||
434 | * scsi_host_set_state() | ||
433 | */ | 435 | */ |
434 | enum { | 436 | enum scsi_host_state { |
435 | SHOST_ADD, | 437 | SHOST_CREATED = 1, |
436 | SHOST_DEL, | 438 | SHOST_RUNNING, |
437 | SHOST_CANCEL, | 439 | SHOST_CANCEL, |
440 | SHOST_DEL, | ||
438 | SHOST_RECOVERY, | 441 | SHOST_RECOVERY, |
439 | }; | 442 | }; |
440 | 443 | ||
@@ -575,7 +578,7 @@ struct Scsi_Host { | |||
575 | unsigned int irq; | 578 | unsigned int irq; |
576 | 579 | ||
577 | 580 | ||
578 | unsigned long shost_state; | 581 | enum scsi_host_state shost_state; |
579 | 582 | ||
580 | /* ldm bits */ | 583 | /* ldm bits */ |
581 | struct device shost_gendev; | 584 | struct device shost_gendev; |
@@ -633,6 +636,7 @@ extern void scsi_remove_host(struct Scsi_Host *); | |||
633 | extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *); | 636 | extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *); |
634 | extern void scsi_host_put(struct Scsi_Host *t); | 637 | extern void scsi_host_put(struct Scsi_Host *t); |
635 | extern struct Scsi_Host *scsi_host_lookup(unsigned short); | 638 | extern struct Scsi_Host *scsi_host_lookup(unsigned short); |
639 | extern const char *scsi_host_state_name(enum scsi_host_state); | ||
636 | 640 | ||
637 | extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *); | 641 | extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *); |
638 | 642 | ||