#include <linux/mm.h>
#include <linux/init.h>
#include <linux/completion.h>
+#include <linux/transport_class.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
set_bit(SHOST_DEL, &shost->shost_state);
+ transport_unregister_device(&shost->shost_gendev);
class_device_unregister(&shost->shost_classdev);
- if (shost->transport_classdev.class)
- class_device_unregister(&shost->transport_classdev);
device_del(&shost->shost_gendev);
}
+EXPORT_SYMBOL(scsi_remove_host);
/**
* scsi_add_host - add a scsi host
GFP_KERNEL)) == NULL)
goto out_del_classdev;
- if (shost->transportt->host_setup)
- shost->transportt->host_setup(shost);
+ if (shost->transportt->create_work_queue) {
+ snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d",
+ shost->host_no);
+ shost->work_q = create_singlethread_workqueue(
+ shost->work_q_name);
+ if (!shost->work_q)
+ goto out_free_shost_data;
+ }
error = scsi_sysfs_add_host(shost);
if (error)
- goto out_del_classdev;
+ goto out_destroy_host;
scsi_proc_host_add(shost);
return error;
+ out_destroy_host:
+ if (shost->work_q)
+ destroy_workqueue(shost->work_q);
+ out_free_shost_data:
+ kfree(shost->shost_data);
out_del_classdev:
class_device_del(&shost->shost_classdev);
out_del_gendev:
out:
return error;
}
+EXPORT_SYMBOL(scsi_add_host);
static void scsi_host_dev_release(struct device *dev)
{
shost->eh_notify = NULL;
}
+ if (shost->work_q)
+ destroy_workqueue(shost->work_q);
+
scsi_proc_hostdir_rm(shost->hostt);
scsi_destroy_command_freelist(shost);
kfree(shost->shost_data);
spin_lock_init(&shost->default_lock);
scsi_assign_lock(shost, &shost->default_lock);
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);
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;
kfree(shost);
return NULL;
}
+EXPORT_SYMBOL(scsi_host_alloc);
struct Scsi_Host *scsi_register(struct scsi_host_template *sht, int privsize)
{
list_add_tail(&shost->sht_legacy_list, &sht->legacy_hosts);
return shost;
}
+EXPORT_SYMBOL(scsi_register);
void scsi_unregister(struct Scsi_Host *shost)
{
list_del(&shost->sht_legacy_list);
scsi_host_put(shost);
}
+EXPORT_SYMBOL(scsi_unregister);
/**
* scsi_host_lookup - get a reference to a Scsi_Host by host no
return shost;
}
+EXPORT_SYMBOL(scsi_host_lookup);
/**
* scsi_host_get - inc a Scsi_Host ref count
return NULL;
return shost;
}
+EXPORT_SYMBOL(scsi_host_get);
/**
* scsi_host_put - dec a Scsi_Host ref count
{
put_device(&shost->shost_gendev);
}
+EXPORT_SYMBOL(scsi_host_put);
int scsi_init_hosts(void)
{
{
class_unregister(&shost_class);
}
+
+int scsi_is_host_device(const struct device *dev)
+{
+ return dev->release == scsi_host_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_host_device);
+
+/**
+ * scsi_queue_work - Queue work to the Scsi_Host workqueue.
+ * @shost: Pointer to Scsi_Host.
+ * @work: Work to queue for execution.
+ *
+ * Return value:
+ * 0 on success / != 0 for error
+ **/
+int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work)
+{
+ if (unlikely(!shost->work_q)) {
+ printk(KERN_ERR
+ "ERROR: Scsi host '%s' attempted to queue scsi-work, "
+ "when no workqueue created.\n", shost->hostt->name);
+ dump_stack();
+
+ return -EINVAL;
+ }
+
+ return queue_work(shost->work_q, work);
+}
+EXPORT_SYMBOL_GPL(scsi_queue_work);
+
+/**
+ * scsi_flush_work - Flush a Scsi_Host's workqueue.
+ * @shost: Pointer to Scsi_Host.
+ **/
+void scsi_flush_work(struct Scsi_Host *shost)
+{
+ if (!shost->work_q) {
+ printk(KERN_ERR
+ "ERROR: Scsi host '%s' attempted to flush scsi-work, "
+ "when no workqueue created.\n", shost->hostt->name);
+ dump_stack();
+ return;
+ }
+
+ flush_workqueue(shost->work_q);
+}
+EXPORT_SYMBOL_GPL(scsi_flush_work);