[SCSI] host state model update: replace old host bitmap state
Migrate the current SCSI host state model to a model like SCSI device is using. Signed-off-by: Mike Anderson <andmike@us.ibm.com> Rejections fixed up and Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
parent
5dbffcd83d
commit
d330187408
8 changed files with 162 additions and 21 deletions
|
@ -51,6 +51,82 @@ static struct class shost_class = {
|
||||||
.release = scsi_host_cls_release,
|
.release = scsi_host_cls_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scsi_host_set_state - Take the given host through the host
|
||||||
|
* state model.
|
||||||
|
* @shost: scsi host to change the state of.
|
||||||
|
* @state: state to change to.
|
||||||
|
*
|
||||||
|
* Returns zero if unsuccessful or an error if the requested
|
||||||
|
* transition is illegal.
|
||||||
|
**/
|
||||||
|
int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
|
||||||
|
{
|
||||||
|
enum scsi_host_state oldstate = shost->shost_state;
|
||||||
|
|
||||||
|
if (state == oldstate)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case SHOST_CREATED:
|
||||||
|
/* There are no legal states that come back to
|
||||||
|
* created. This is the manually initialised start
|
||||||
|
* state */
|
||||||
|
goto illegal;
|
||||||
|
|
||||||
|
case SHOST_RUNNING:
|
||||||
|
switch (oldstate) {
|
||||||
|
case SHOST_CREATED:
|
||||||
|
case SHOST_RECOVERY:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto illegal;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHOST_RECOVERY:
|
||||||
|
switch (oldstate) {
|
||||||
|
case SHOST_RUNNING:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto illegal;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHOST_CANCEL:
|
||||||
|
switch (oldstate) {
|
||||||
|
case SHOST_CREATED:
|
||||||
|
case SHOST_RUNNING:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto illegal;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHOST_DEL:
|
||||||
|
switch (oldstate) {
|
||||||
|
case SHOST_CANCEL:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto illegal;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
shost->shost_state = state;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
illegal:
|
||||||
|
SCSI_LOG_ERROR_RECOVERY(1,
|
||||||
|
dev_printk(KERN_ERR, &shost->shost_gendev,
|
||||||
|
"Illegal host state transition"
|
||||||
|
"%s->%s\n",
|
||||||
|
scsi_host_state_name(oldstate),
|
||||||
|
scsi_host_state_name(state)));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(scsi_host_set_state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* scsi_host_cancel - cancel outstanding IO to this host
|
* scsi_host_cancel - cancel outstanding IO to this host
|
||||||
* @shost: pointer to struct Scsi_Host
|
* @shost: pointer to struct Scsi_Host
|
||||||
|
@ -60,12 +136,11 @@ static void scsi_host_cancel(struct Scsi_Host *shost, int recovery)
|
||||||
{
|
{
|
||||||
struct scsi_device *sdev;
|
struct scsi_device *sdev;
|
||||||
|
|
||||||
set_bit(SHOST_CANCEL, &shost->shost_state);
|
scsi_host_set_state(shost, SHOST_CANCEL);
|
||||||
shost_for_each_device(sdev, shost) {
|
shost_for_each_device(sdev, shost) {
|
||||||
scsi_device_cancel(sdev, recovery);
|
scsi_device_cancel(sdev, recovery);
|
||||||
}
|
}
|
||||||
wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY,
|
wait_event(shost->host_wait, (shost->shost_state != SHOST_RECOVERY));
|
||||||
&shost->shost_state)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,7 +153,7 @@ void scsi_remove_host(struct Scsi_Host *shost)
|
||||||
scsi_host_cancel(shost, 0);
|
scsi_host_cancel(shost, 0);
|
||||||
scsi_proc_host_rm(shost);
|
scsi_proc_host_rm(shost);
|
||||||
|
|
||||||
set_bit(SHOST_DEL, &shost->shost_state);
|
scsi_host_set_state(shost, SHOST_DEL);
|
||||||
|
|
||||||
transport_unregister_device(&shost->shost_gendev);
|
transport_unregister_device(&shost->shost_gendev);
|
||||||
class_device_unregister(&shost->shost_classdev);
|
class_device_unregister(&shost->shost_classdev);
|
||||||
|
@ -115,7 +190,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
set_bit(SHOST_ADD, &shost->shost_state);
|
scsi_host_set_state(shost, SHOST_RUNNING);
|
||||||
get_device(shost->shost_gendev.parent);
|
get_device(shost->shost_gendev.parent);
|
||||||
|
|
||||||
error = class_device_add(&shost->shost_classdev);
|
error = class_device_add(&shost->shost_classdev);
|
||||||
|
@ -226,6 +301,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
|
||||||
|
|
||||||
spin_lock_init(&shost->default_lock);
|
spin_lock_init(&shost->default_lock);
|
||||||
scsi_assign_lock(shost, &shost->default_lock);
|
scsi_assign_lock(shost, &shost->default_lock);
|
||||||
|
shost->shost_state = SHOST_CREATED;
|
||||||
INIT_LIST_HEAD(&shost->__devices);
|
INIT_LIST_HEAD(&shost->__devices);
|
||||||
INIT_LIST_HEAD(&shost->__targets);
|
INIT_LIST_HEAD(&shost->__targets);
|
||||||
INIT_LIST_HEAD(&shost->eh_cmd_q);
|
INIT_LIST_HEAD(&shost->eh_cmd_q);
|
||||||
|
@ -382,7 +458,7 @@ EXPORT_SYMBOL(scsi_host_lookup);
|
||||||
**/
|
**/
|
||||||
struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
|
struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
|
||||||
{
|
{
|
||||||
if (test_bit(SHOST_DEL, &shost->shost_state) ||
|
if ((shost->shost_state == SHOST_DEL) ||
|
||||||
!get_device(&shost->shost_gendev))
|
!get_device(&shost->shost_gendev))
|
||||||
return NULL;
|
return NULL;
|
||||||
return shost;
|
return shost;
|
||||||
|
|
|
@ -627,7 +627,7 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
|
||||||
spin_lock_irqsave(host->host_lock, flags);
|
spin_lock_irqsave(host->host_lock, flags);
|
||||||
scsi_cmd_get_serial(host, cmd);
|
scsi_cmd_get_serial(host, cmd);
|
||||||
|
|
||||||
if (unlikely(test_bit(SHOST_CANCEL, &host->shost_state))) {
|
if (unlikely(host->shost_state == SHOST_CANCEL)) {
|
||||||
cmd->result = (DID_NO_CONNECT << 16);
|
cmd->result = (DID_NO_CONNECT << 16);
|
||||||
scsi_done(cmd);
|
scsi_done(cmd);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -75,7 +75,7 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
|
||||||
|
|
||||||
scmd->eh_eflags |= eh_flag;
|
scmd->eh_eflags |= eh_flag;
|
||||||
list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
|
list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
|
||||||
set_bit(SHOST_RECOVERY, &shost->shost_state);
|
scsi_host_set_state(shost, SHOST_RECOVERY);
|
||||||
shost->host_failed++;
|
shost->host_failed++;
|
||||||
scsi_eh_wakeup(shost);
|
scsi_eh_wakeup(shost);
|
||||||
spin_unlock_irqrestore(shost->host_lock, flags);
|
spin_unlock_irqrestore(shost->host_lock, flags);
|
||||||
|
@ -197,7 +197,8 @@ int scsi_block_when_processing_errors(struct scsi_device *sdev)
|
||||||
{
|
{
|
||||||
int online;
|
int online;
|
||||||
|
|
||||||
wait_event(sdev->host->host_wait, (!test_bit(SHOST_RECOVERY, &sdev->host->shost_state)));
|
wait_event(sdev->host->host_wait, (sdev->host->shost_state !=
|
||||||
|
SHOST_RECOVERY));
|
||||||
|
|
||||||
online = scsi_device_online(sdev);
|
online = scsi_device_online(sdev);
|
||||||
|
|
||||||
|
@ -1458,7 +1459,7 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
|
||||||
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n",
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n",
|
||||||
__FUNCTION__));
|
__FUNCTION__));
|
||||||
|
|
||||||
clear_bit(SHOST_RECOVERY, &shost->shost_state);
|
scsi_host_set_state(shost, SHOST_RUNNING);
|
||||||
|
|
||||||
wake_up(&shost->host_wait);
|
wake_up(&shost->host_wait);
|
||||||
|
|
||||||
|
|
|
@ -475,8 +475,7 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
|
||||||
* error processing, as long as the device was opened
|
* error processing, as long as the device was opened
|
||||||
* non-blocking */
|
* non-blocking */
|
||||||
if (filp && filp->f_flags & O_NONBLOCK) {
|
if (filp && filp->f_flags & O_NONBLOCK) {
|
||||||
if (test_bit(SHOST_RECOVERY,
|
if (sdev->host->shost_state == SHOST_RECOVERY)
|
||||||
&sdev->host->shost_state))
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
} else if (!scsi_block_when_processing_errors(sdev))
|
} else if (!scsi_block_when_processing_errors(sdev))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -348,7 +348,7 @@ void scsi_device_unbusy(struct scsi_device *sdev)
|
||||||
|
|
||||||
spin_lock_irqsave(shost->host_lock, flags);
|
spin_lock_irqsave(shost->host_lock, flags);
|
||||||
shost->host_busy--;
|
shost->host_busy--;
|
||||||
if (unlikely(test_bit(SHOST_RECOVERY, &shost->shost_state) &&
|
if (unlikely((shost->shost_state == SHOST_RECOVERY) &&
|
||||||
shost->host_failed))
|
shost->host_failed))
|
||||||
scsi_eh_wakeup(shost);
|
scsi_eh_wakeup(shost);
|
||||||
spin_unlock(shost->host_lock);
|
spin_unlock(shost->host_lock);
|
||||||
|
@ -1207,7 +1207,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q,
|
||||||
struct Scsi_Host *shost,
|
struct Scsi_Host *shost,
|
||||||
struct scsi_device *sdev)
|
struct scsi_device *sdev)
|
||||||
{
|
{
|
||||||
if (test_bit(SHOST_RECOVERY, &shost->shost_state))
|
if (shost->shost_state == SHOST_RECOVERY)
|
||||||
return 0;
|
return 0;
|
||||||
if (shost->host_busy == 0 && shost->host_blocked) {
|
if (shost->host_busy == 0 && shost->host_blocked) {
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -48,6 +48,30 @@ const char *scsi_device_state_name(enum scsi_device_state state)
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
enum scsi_host_state value;
|
||||||
|
char *name;
|
||||||
|
} shost_states[] = {
|
||||||
|
{ SHOST_CREATED, "created" },
|
||||||
|
{ SHOST_RUNNING, "running" },
|
||||||
|
{ SHOST_CANCEL, "cancel" },
|
||||||
|
{ SHOST_DEL, "deleted" },
|
||||||
|
{ SHOST_RECOVERY, "recovery" },
|
||||||
|
};
|
||||||
|
const char *scsi_host_state_name(enum scsi_host_state state)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *name = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(shost_states)/sizeof(shost_states[0]); i++) {
|
||||||
|
if (shost_states[i].value == state) {
|
||||||
|
name = shost_states[i].name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
static int check_set(unsigned int *val, char *src)
|
static int check_set(unsigned int *val, char *src)
|
||||||
{
|
{
|
||||||
char *last;
|
char *last;
|
||||||
|
@ -124,6 +148,43 @@ static ssize_t store_scan(struct class_device *class_dev, const char *buf,
|
||||||
};
|
};
|
||||||
static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan);
|
static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan);
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
store_shost_state(struct class_device *class_dev, const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct Scsi_Host *shost = class_to_shost(class_dev);
|
||||||
|
enum scsi_host_state state = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(shost_states)/sizeof(shost_states[0]); i++) {
|
||||||
|
const int len = strlen(shost_states[i].name);
|
||||||
|
if (strncmp(shost_states[i].name, buf, len) == 0 &&
|
||||||
|
buf[len] == '\n') {
|
||||||
|
state = shost_states[i].value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!state)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (scsi_host_set_state(shost, state))
|
||||||
|
return -EINVAL;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
show_shost_state(struct class_device *class_dev, char *buf)
|
||||||
|
{
|
||||||
|
struct Scsi_Host *shost = class_to_shost(class_dev);
|
||||||
|
const char *name = scsi_host_state_name(shost->shost_state);
|
||||||
|
|
||||||
|
if (!name)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return snprintf(buf, 20, "%s\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_shost_state, store_shost_state);
|
||||||
|
|
||||||
shost_rd_attr(unique_id, "%u\n");
|
shost_rd_attr(unique_id, "%u\n");
|
||||||
shost_rd_attr(host_busy, "%hu\n");
|
shost_rd_attr(host_busy, "%hu\n");
|
||||||
shost_rd_attr(cmd_per_lun, "%hd\n");
|
shost_rd_attr(cmd_per_lun, "%hd\n");
|
||||||
|
@ -139,6 +200,7 @@ static struct class_device_attribute *scsi_sysfs_shost_attrs[] = {
|
||||||
&class_device_attr_unchecked_isa_dma,
|
&class_device_attr_unchecked_isa_dma,
|
||||||
&class_device_attr_proc_name,
|
&class_device_attr_proc_name,
|
||||||
&class_device_attr_scan,
|
&class_device_attr_scan,
|
||||||
|
&class_device_attr_state,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1027,8 +1027,7 @@ sg_ioctl(struct inode *inode, struct file *filp,
|
||||||
if (sdp->detached)
|
if (sdp->detached)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
if (filp->f_flags & O_NONBLOCK) {
|
if (filp->f_flags & O_NONBLOCK) {
|
||||||
if (test_bit(SHOST_RECOVERY,
|
if (sdp->device->host->shost_state == SHOST_RECOVERY)
|
||||||
&sdp->device->host->shost_state))
|
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
} else if (!scsi_block_when_processing_errors(sdp->device))
|
} else if (!scsi_block_when_processing_errors(sdp->device))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
|
@ -429,12 +429,15 @@ struct scsi_host_template {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* shost states
|
* shost state: If you alter this, you also need to alter scsi_sysfs.c
|
||||||
|
* (for the ascii descriptions) and the state model enforcer:
|
||||||
|
* scsi_host_set_state()
|
||||||
*/
|
*/
|
||||||
enum {
|
enum scsi_host_state {
|
||||||
SHOST_ADD,
|
SHOST_CREATED = 1,
|
||||||
SHOST_DEL,
|
SHOST_RUNNING,
|
||||||
SHOST_CANCEL,
|
SHOST_CANCEL,
|
||||||
|
SHOST_DEL,
|
||||||
SHOST_RECOVERY,
|
SHOST_RECOVERY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -575,7 +578,7 @@ struct Scsi_Host {
|
||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
|
|
||||||
|
|
||||||
unsigned long shost_state;
|
enum scsi_host_state shost_state;
|
||||||
|
|
||||||
/* ldm bits */
|
/* ldm bits */
|
||||||
struct device shost_gendev;
|
struct device shost_gendev;
|
||||||
|
@ -633,6 +636,7 @@ extern void scsi_remove_host(struct Scsi_Host *);
|
||||||
extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *);
|
extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *);
|
||||||
extern void scsi_host_put(struct Scsi_Host *t);
|
extern void scsi_host_put(struct Scsi_Host *t);
|
||||||
extern struct Scsi_Host *scsi_host_lookup(unsigned short);
|
extern struct Scsi_Host *scsi_host_lookup(unsigned short);
|
||||||
|
extern const char *scsi_host_state_name(enum scsi_host_state);
|
||||||
|
|
||||||
extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *);
|
extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue