This rework prepares support of multiple logical units per target. Instead of one instance of struct Scsi_Host per target, only one Scsi_Host global to fw-sbp2 is now used. We could also use one Scsi_Host per FireWire host, but that would add more overhead for no real benefit. The following user-visible changes result: - The generic device of the Scsi_Host is registered as a platform device. Hence it and the target devices and logical unit devices will be sitting below /sys/devices/platform/host*/ rather than within the PCI devices tree. - In the SCSI stack's H:C:T:L tuple, the H is now constant for all fw-sbp2 devices but the T is unique. We currently allocate T as stupidly as the H was allocated before: It is simply increased whenever a target is added. Neither of these changes should affect userspace, because none of the mentioned device properties have been of any interest to userspace before. Signed-off-by: Stefan Richter <stefanr@xxxxxxxxxxxxxxxxx> --- requires patches "firewire: fw-sbp2: sanitize list handling" (update) http://marc.info/?l=linux1394-devel&m=118566666024249 "firewire: remove unused macros" http://marc.info/?l=linux1394-devel&m=118540673511987 update: fix memory leak in release_sbp2_device drivers/firewire/fw-sbp2.c | 118 +++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 48 deletions(-) Index: linux/drivers/firewire/fw-sbp2.c =================================================================== --- linux.orig/drivers/firewire/fw-sbp2.c +++ linux/drivers/firewire/fw-sbp2.c @@ -28,6 +28,7 @@ * and many others. */ +#include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -39,6 +40,8 @@ #include <linux/string.h> #include <linux/timer.h> +#include <asm/atomic.h> + #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_dbg.h> @@ -71,6 +74,7 @@ struct sbp2_device { struct fw_unit *unit; struct fw_address_handler address_handler; struct list_head orb_list; + struct scsi_device *sdev; u64 management_agent_address; u64 command_block_agent_address; u32 workarounds; @@ -522,34 +526,35 @@ static int sbp2_agent_reset(struct fw_un return 0; } -static void sbp2_reconnect(struct work_struct *work); -static struct scsi_host_template scsi_driver_template; - static void release_sbp2_device(struct kref *kref) { struct sbp2_device *sd = container_of(kref, struct sbp2_device, kref); - struct Scsi_Host *host = - container_of((void *)sd, struct Scsi_Host, hostdata[0]); - scsi_remove_host(host); + if (sd->sdev) + scsi_remove_device(sd->sdev); + sbp2_send_management_orb(sd->unit, sd->node_id, sd->generation, SBP2_LOGOUT_REQUEST, sd->login_id, NULL); fw_core_remove_address_handler(&sd->address_handler); fw_notify("removed sbp2 unit %s\n", sd->unit->device.bus_id); put_device(&sd->unit->device); - scsi_host_put(host); + kfree(sd); } +static void sbp2_reconnect(struct work_struct *work); + +static struct Scsi_Host *sbp2_shost; +static atomic_t sbp2_starget_id = ATOMIC_INIT(-1); + static void sbp2_login(struct work_struct *work) { struct sbp2_device *sd = container_of(work, struct sbp2_device, work.work); - struct Scsi_Host *host = - container_of((void *)sd, struct Scsi_Host, hostdata[0]); + struct scsi_device *sdev; struct fw_unit *unit = sd->unit; struct fw_device *device = fw_device(unit->device.parent); struct sbp2_login_response response; - int generation, node_id, local_node_id, lun, retval; + int generation, node_id, local_node_id, lun; /* FIXME: Make this work for multi-lun devices. */ lun = 0; @@ -597,10 +602,9 @@ static void sbp2_login(struct work_struc PREPARE_DELAYED_WORK(&sd->work, sbp2_reconnect); sbp2_agent_reset(unit); - /* FIXME: Loop over luns here. */ - lun = 0; - retval = scsi_add_device(host, 0, 0, lun); - if (retval < 0) { + sdev = __scsi_add_device(sbp2_shost, 0, + atomic_inc_return(&sbp2_starget_id), lun, sd); + if (IS_ERR(sdev)) { sbp2_send_management_orb(unit, sd->node_id, sd->generation, SBP2_LOGOUT_REQUEST, sd->login_id, NULL); @@ -609,26 +613,28 @@ static void sbp2_login(struct work_struc * retry login on bus reset. */ PREPARE_DELAYED_WORK(&sd->work, sbp2_login); + } else { + sd->sdev = sdev; + scsi_device_put(sdev); } kref_put(&sd->kref, release_sbp2_device); } +/* FIXME: Loop over luns here. */ static int sbp2_probe(struct device *dev) { struct fw_unit *unit = fw_unit(dev); struct fw_device *device = fw_device(unit->device.parent); struct sbp2_device *sd; struct fw_csr_iterator ci; - struct Scsi_Host *host; - int i, key, value, err; + int i, key, value, error; u32 model, firmware_revision; - err = -ENOMEM; - host = scsi_host_alloc(&scsi_driver_template, sizeof(*sd)); - if (host == NULL) - goto fail; + error = -ENOMEM; + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) + goto out_error; - sd = (struct sbp2_device *) host->hostdata; unit->device.driver_data = sd; sd->unit = unit; INIT_LIST_HEAD(&sd->orb_list); @@ -638,18 +644,14 @@ static int sbp2_probe(struct device *dev sd->address_handler.address_callback = sbp2_status_write; sd->address_handler.callback_data = sd; - err = fw_core_add_address_handler(&sd->address_handler, - &fw_high_memory_region); - if (err < 0) - goto fail_host; - - err = fw_device_enable_phys_dma(device); - if (err < 0) - goto fail_address_handler; - - err = scsi_add_host(host, &unit->device); - if (err < 0) - goto fail_address_handler; + error = fw_core_add_address_handler(&sd->address_handler, + &fw_high_memory_region); + if (error < 0) + goto out_free; + + error = fw_device_enable_phys_dma(device); + if (error < 0) + goto out_remove_address_handler; /* * Scan unit directory to get management agent address, @@ -704,12 +706,12 @@ static int sbp2_probe(struct device *dev return 0; - fail_address_handler: + out_remove_address_handler: fw_core_remove_address_handler(&sd->address_handler); - fail_host: - scsi_host_put(host); - fail: - return err; + out_free: + kfree(sd); + out_error: + return error; } static int sbp2_remove(struct device *dev) @@ -890,8 +892,7 @@ complete_command_orb(struct sbp2_orb *ba static int sbp2_command_orb_map_scatterlist(struct sbp2_command_orb *orb) { - struct sbp2_device *sd = - (struct sbp2_device *)orb->cmd->device->host->hostdata; + struct sbp2_device *sd = orb->cmd->device->hostdata; struct fw_unit *unit = sd->unit; struct fw_device *device = fw_device(unit->device.parent); struct scatterlist *sg; @@ -978,8 +979,7 @@ static int sbp2_command_orb_map_scatterl static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) { - struct sbp2_device *sd = - (struct sbp2_device *)cmd->device->host->hostdata; + struct sbp2_device *sd = cmd->device->hostdata; struct fw_unit *unit = sd->unit; struct fw_device *device = fw_device(unit->device.parent); struct sbp2_command_orb *orb; @@ -1060,7 +1060,7 @@ static int sbp2_scsi_queuecommand(struct static int sbp2_scsi_slave_alloc(struct scsi_device *sdev) { - struct sbp2_device *sd = (struct sbp2_device *)sdev->host->hostdata; + struct sbp2_device *sd = sdev->hostdata; sdev->allow_restart = 1; @@ -1071,7 +1071,7 @@ static int sbp2_scsi_slave_alloc(struct static int sbp2_scsi_slave_configure(struct scsi_device *sdev) { - struct sbp2_device *sd = (struct sbp2_device *)sdev->host->hostdata; + struct sbp2_device *sd = sdev->hostdata; struct fw_unit *unit = sd->unit; sdev->use_10_for_rw = 1; @@ -1096,8 +1096,7 @@ static int sbp2_scsi_slave_configure(str */ static int sbp2_scsi_abort(struct scsi_cmnd *cmd) { - struct sbp2_device *sd = - (struct sbp2_device *)cmd->device->host->hostdata; + struct sbp2_device *sd = cmd->device->hostdata; struct fw_unit *unit = sd->unit; fw_notify("sbp2_scsi_abort\n"); @@ -1128,7 +1127,7 @@ sbp2_sysfs_ieee1394_id_show(struct devic if (!sdev) return 0; - sd = (struct sbp2_device *)sdev->host->hostdata; + sd = sdev->hostdata; unit = sd->unit; device = fw_device(unit->device.parent); @@ -1187,12 +1186,35 @@ MODULE_ALIAS("sbp2"); static int __init sbp2_init(void) { - return driver_register(&sbp2_driver.driver); + int error = -ENOMEM; + + sbp2_shost = scsi_host_alloc(&scsi_driver_template, 0); + if (!sbp2_shost) + goto out_error; + + error = scsi_add_host(sbp2_shost, NULL); + if (error) + goto out_host_put; + + error = driver_register(&sbp2_driver.driver); + if (error) + goto out_remove_host; + + return 0; + + out_remove_host: + scsi_remove_host(sbp2_shost); + out_host_put: + scsi_host_put(sbp2_shost); + out_error: + return error; } static void __exit sbp2_cleanup(void) { driver_unregister(&sbp2_driver.driver); + scsi_remove_host(sbp2_shost); + scsi_host_put(sbp2_shost); } module_init(sbp2_init); -- Stefan Richter -=====-=-=== -=== ===-= http://arcgraph.de/sr/ - To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html