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 | ||
