Following discussions which resulted from the: [RFC] target code updates to support scanned targets http://marc.theaimsgroup.com/?l=linux-scsi&m=110850749515984&w=2
thread, the overall consensus seems to be that transport-classes should support a 'true-hotplug' mechanism of device discovery and removal (this involves both registration and lun-scanning). In order to facilitate this ability and to not constrain the LLDD from the typical restrictions of the storage scanning process: * must be done from process context -- depending on transport type, discovery can occur from a non-process context * potentially _long_ scan times -- even if discovery is done from a 'sleeping' capable context, halting a LLDD for discovery purposes is typically undesirable. I'd like to propose the addition of a per-Scsi_Host work-queue to manage these scanning as well as any other (relevant) lower-level-driver differed requests. The attached patch adds: 1) a new scsi-host template member 'create_work_queue' which when set will create a single-threaded (the per-CPU option seemed too heavyweight) work-queue. 2) an exported helper function scsi_queue_work() to queue the work to the scsi-host work-queue. the interface was kept flexible -- queuing of a work_struct object rather than adding small wrapper functions to perform distinct operations (i.e. scsi_scan_target_deferred(...), etc) so that any 'deferred' action could be requested by a transport-class or LLDD. Example usage: scsi_transport_fc.h: struct fc_rport { ... struct work_struct scan_work; } __attribute__((aligned(sizeof(unsigned long)))); scsi_transport_fc.c: ... static void fc_scan_rport_work(void *data) { struct fc_rport *rport = (struct fc_rport *)data; scsi_scan_target(&rport->dev, rport->channel, rport->scsi_target_id, SCAN_WILD_CARD, 0); } struct fc_rport * fc_create_rport(struct Scsi_Host *shost, int channel, struct fc_rport_identifiers *ids) { ... INIT_WORK(&rport->scan_work, fc_scan_rport_work, rport); ... void fc_remote_port_unblock(struct fc_rport *rport) { struct Scsi_Host *shost = rport_to_shost(rport); ... if (rport->port_state == FC_PORTSTATE_OFFLINE) /* Initiate a full rescan, as all * scsi_target objects have been previously * torn-down. */ scsi_queue_work(shost, &rport->scan_work); else scsi_target_unblock(&rport->dev); Anyway, comments? -- Andrew Vasquez ===== drivers/scsi/hosts.c 1.107 vs edited ===== --- 1.107/drivers/scsi/hosts.c 2005-01-18 11:15:06 -08:00 +++ edited/drivers/scsi/hosts.c 2005-02-21 15:10:38 -08:00 @@ -160,6 +160,9 @@ static void scsi_host_dev_release(struct shost->eh_notify = NULL; } + if (shost->create_work_queue) + destroy_workqueue(shost->work_q); + scsi_proc_hostdir_rm(shost->hostt); scsi_destroy_command_freelist(shost); kfree(shost->shost_data); @@ -247,6 +250,7 @@ struct Scsi_Host *scsi_host_alloc(struct shost->cmd_per_lun = sht->cmd_per_lun; shost->unchecked_isa_dma = sht->unchecked_isa_dma; shost->use_clustering = sht->use_clustering; + shost->create_work_queue = sht->create_work_queue; if (sht->max_host_blocked) shost->max_host_blocked = sht->max_host_blocked; @@ -285,16 +289,27 @@ struct Scsi_Host *scsi_host_alloc(struct snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d", shost->host_no); + if (shost->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 fail_destroy_freelist; + } + shost->eh_notify = &complete; rval = kernel_thread(scsi_error_handler, shost, 0); if (rval < 0) - goto fail_destroy_freelist; + goto fail_destroy_workqueue; wait_for_completion(&complete); shost->eh_notify = NULL; scsi_proc_hostdir_add(shost->hostt); return shost; + fail_destroy_workqueue: + destroy_workqueue(shost->work_q); fail_destroy_freelist: scsi_destroy_command_freelist(shost); fail_kfree: @@ -392,3 +407,26 @@ int scsi_is_host_device(const struct 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->create_work_queue)) { + 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); ===== include/scsi/scsi_host.h 1.26 vs edited ===== --- 1.26/include/scsi/scsi_host.h 2005-01-19 08:01:21 -08:00 +++ edited/include/scsi/scsi_host.h 2005-02-21 12:13:32 -08:00 @@ -4,6 +4,7 @@ #include <linux/device.h> #include <linux/list.h> #include <linux/types.h> +#include <linux/workqueue.h> struct block_device; struct module; @@ -363,6 +364,11 @@ struct scsi_host_template { unsigned skip_settle_delay:1; /* + * True if a work-queue should be created for shost processing. + */ + unsigned create_work_queue:1; + + /* * Countdown for host blocking with no commands outstanding */ unsigned int max_host_blocked; @@ -501,6 +507,11 @@ struct Scsi_Host { */ unsigned reverse_ordering:1; + unsigned create_work_queue:1; + + char work_q_name[KOBJ_NAME_LEN]; + struct workqueue_struct *work_q; + /* * Host has rejected a command because it was busy. */ @@ -598,6 +609,7 @@ extern void scsi_free_host_dev(struct sc extern struct scsi_device *scsi_get_host_dev(struct Scsi_Host *); int scsi_is_host_device(const struct device *); +extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *); /* legacy interfaces */ extern struct Scsi_Host *scsi_register(struct scsi_host_template *, int); - To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html