X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fscsi%2Fhosts.c;h=bd8e7f323c69fcd607ba6124f7a83339809a8abd;hb=9413d7b8aa777dd1fc7db9563ce5e80d769fe7b5;hp=d7a38b6713f958b1c4023544b3de1869b5a22bcf;hpb=52c1da39534fb382c061de58b65f678ad74b59f5;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index d7a38b6..bd8e7f3 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -24,11 +24,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -52,21 +54,100 @@ static struct class shost_class = { }; /** - * scsi_host_cancel - cancel outstanding IO to this host - * @shost: pointer to struct Scsi_Host - * recovery: recovery requested to run. + * 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. **/ -static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) +int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) { - struct scsi_device *sdev; + 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: + case SHOST_CANCEL_RECOVERY: + break; + default: + goto illegal; + } + break; - set_bit(SHOST_CANCEL, &shost->shost_state); - shost_for_each_device(sdev, shost) { - scsi_device_cancel(sdev, recovery); + case SHOST_DEL: + switch (oldstate) { + case SHOST_CANCEL: + case SHOST_DEL_RECOVERY: + break; + default: + goto illegal; + } + break; + + case SHOST_CANCEL_RECOVERY: + switch (oldstate) { + case SHOST_CANCEL: + case SHOST_RECOVERY: + break; + default: + goto illegal; + } + break; + + case SHOST_DEL_RECOVERY: + switch (oldstate) { + case SHOST_CANCEL_RECOVERY: + break; + default: + goto illegal; + } + break; } - wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY, - &shost->shost_state))); + shost->shost_state = state; + return 0; + + illegal: + SCSI_LOG_ERROR_RECOVERY(1, + shost_printk(KERN_ERR, shost, + "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_remove_host - remove a scsi host @@ -74,15 +155,29 @@ static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) **/ void scsi_remove_host(struct Scsi_Host *shost) { + unsigned long flags; + mutex_lock(&shost->scan_mutex); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_CANCEL)) + if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) { + spin_unlock_irqrestore(shost->host_lock, flags); + mutex_unlock(&shost->scan_mutex); + return; + } + spin_unlock_irqrestore(shost->host_lock, flags); + mutex_unlock(&shost->scan_mutex); scsi_forget_host(shost); - scsi_host_cancel(shost, 0); scsi_proc_host_rm(shost); - set_bit(SHOST_DEL, &shost->shost_state); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_DEL)) + BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY)); + spin_unlock_irqrestore(shost->host_lock, flags); transport_unregister_device(&shost->shost_gendev); class_device_unregister(&shost->shost_classdev); device_del(&shost->shost_gendev); + scsi_proc_hostdir_rm(shost->hostt); } EXPORT_SYMBOL(scsi_remove_host); @@ -115,7 +210,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) if (error) goto out; - set_bit(SHOST_ADD, &shost->shost_state); + scsi_host_set_state(shost, SHOST_RUNNING); get_device(shost->shost_gendev.parent); error = class_device_add(&shost->shost_classdev); @@ -164,27 +259,21 @@ static void scsi_host_dev_release(struct device *dev) struct Scsi_Host *shost = dev_to_shost(dev); struct device *parent = dev->parent; - if (shost->ehandler) { - DECLARE_COMPLETION(sem); - shost->eh_notify = &sem; - shost->eh_kill = 1; - up(shost->eh_wait); - wait_for_completion(&sem); - shost->eh_notify = NULL; - } - + if (shost->ehandler) + kthread_stop(shost->ehandler); if (shost->work_q) destroy_workqueue(shost->work_q); + if (shost->uspace_req_q) { + kfree(shost->uspace_req_q->queuedata); + scsi_free_queue(shost->uspace_req_q); + } - scsi_proc_hostdir_rm(shost->hostt); scsi_destroy_command_freelist(shost); + if (shost->bqt) + blk_free_tags(shost->bqt); + kfree(shost->shost_data); - /* - * Some drivers (eg aha1542) do scsi_register()/scsi_unregister() - * during probing without performing a scsi_set_device() in between. - * In this case dev->parent is NULL. - */ if (parent) put_device(parent); kfree(shost); @@ -206,38 +295,26 @@ static void scsi_host_dev_release(struct device *dev) struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) { struct Scsi_Host *shost; - int gfp_mask = GFP_KERNEL, rval; - DECLARE_COMPLETION(complete); + gfp_t gfp_mask = GFP_KERNEL; + int rval; if (sht->unchecked_isa_dma && privsize) gfp_mask |= __GFP_DMA; - /* Check to see if this host has any error handling facilities */ - if (!sht->eh_strategy_handler && !sht->eh_abort_handler && - !sht->eh_device_reset_handler && !sht->eh_bus_reset_handler && - !sht->eh_host_reset_handler) { - printk(KERN_ERR "ERROR: SCSI host `%s' has no error handling\n" - "ERROR: This is not a safe way to run your " - "SCSI host\n" - "ERROR: The error handling must be added to " - "this driver\n", sht->proc_name); - dump_stack(); - } - - shost = kmalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask); + shost = kzalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask); if (!shost) return NULL; - memset(shost, 0, sizeof(struct Scsi_Host) + privsize); - spin_lock_init(&shost->default_lock); - scsi_assign_lock(shost, &shost->default_lock); + shost->host_lock = &shost->default_lock; + spin_lock_init(shost->host_lock); + shost->shost_state = SHOST_CREATED; INIT_LIST_HEAD(&shost->__devices); INIT_LIST_HEAD(&shost->__targets); INIT_LIST_HEAD(&shost->eh_cmd_q); INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); - init_MUTEX(&shost->scan_mutex); + mutex_init(&shost->scan_mutex); shost->host_no = scsi_host_next_hn++; /* XXX(hch): still racy */ shost->dma_channel = 0xff; @@ -264,17 +341,8 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) shost->cmd_per_lun = sht->cmd_per_lun; shost->unchecked_isa_dma = sht->unchecked_isa_dma; shost->use_clustering = sht->use_clustering; - shost->ordered_flush = sht->ordered_flush; shost->ordered_tag = sht->ordered_tag; - /* - * hosts/devices that do queueing must support ordered tags - */ - if (shost->can_queue > 1 && shost->ordered_flush) { - printk(KERN_ERR "scsi: ordered flushes don't support queueing\n"); - shost->ordered_flush = 0; - } - if (sht->max_host_blocked) shost->max_host_blocked = sht->max_host_blocked; else @@ -312,12 +380,12 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d", shost->host_no); - shost->eh_notify = &complete; - rval = kernel_thread(scsi_error_handler, shost, 0); - if (rval < 0) + shost->ehandler = kthread_run(scsi_error_handler, shost, + "scsi_eh_%d", shost->host_no); + if (IS_ERR(shost->ehandler)) { + rval = PTR_ERR(shost->ehandler); goto fail_destroy_freelist; - wait_for_completion(&complete); - shost->eh_notify = NULL; + } scsi_proc_hostdir_add(shost->hostt); return shost; @@ -367,7 +435,7 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) struct class_device *cdev; struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p; - down_read(&class->subsys.rwsem); + down(&class->sem); list_for_each_entry(cdev, &class->children, node) { p = class_to_shost(cdev); if (p->host_no == hostnum) { @@ -375,7 +443,7 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) break; } } - up_read(&class->subsys.rwsem); + up(&class->sem); return shost; } @@ -387,7 +455,7 @@ EXPORT_SYMBOL(scsi_host_lookup); **/ 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)) return NULL; return shost; @@ -426,7 +494,9 @@ EXPORT_SYMBOL(scsi_is_host_device); * @work: Work to queue for execution. * * Return value: - * 0 on success / != 0 for error + * 1 - work queued for execution + * 0 - work is already queued + * -EINVAL - work queue doesn't exist **/ int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work) {