aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/hosts.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/hosts.c')
-rw-r--r--drivers/scsi/hosts.c90
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 **/
59static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) 63int 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}
128EXPORT_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 **/
75void scsi_remove_host(struct Scsi_Host *shost) 134void 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 **/
383struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) 445struct 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;