aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/hosts.c88
-rw-r--r--drivers/scsi/scsi.c2
-rw-r--r--drivers/scsi/scsi_error.c7
-rw-r--r--drivers/scsi/scsi_ioctl.c3
-rw-r--r--drivers/scsi/scsi_lib.c4
-rw-r--r--drivers/scsi/scsi_sysfs.c62
-rw-r--r--drivers/scsi/sg.c3
-rw-r--r--include/scsi/scsi_host.h14
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 **/
63int 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}
128EXPORT_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 **/
383struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) 459struct 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
51static 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};
61const 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
51static int check_set(unsigned int *val, char *src) 75static 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};
125static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); 149static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan);
126 150
151static ssize_t
152store_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
174static ssize_t
175show_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
186static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_shost_state, store_shost_state);
187
127shost_rd_attr(unique_id, "%u\n"); 188shost_rd_attr(unique_id, "%u\n");
128shost_rd_attr(host_busy, "%hu\n"); 189shost_rd_attr(host_busy, "%hu\n");
129shost_rd_attr(cmd_per_lun, "%hd\n"); 190shost_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 */
434enum { 436enum 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 *);
633extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *); 636extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *);
634extern void scsi_host_put(struct Scsi_Host *t); 637extern void scsi_host_put(struct Scsi_Host *t);
635extern struct Scsi_Host *scsi_host_lookup(unsigned short); 638extern struct Scsi_Host *scsi_host_lookup(unsigned short);
639extern const char *scsi_host_state_name(enum scsi_host_state);
636 640
637extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *); 641extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *);
638 642