X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi_transport_fc.c;fp=drivers%2Fscsi%2Fscsi_transport_fc.c;h=13ea64119b730532ab0ee447c50e1ec7ced08b6d;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=b03aa85108e5f0b6860e9d3b405b5087fb6d3aad;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index b03aa8510..13ea64119 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -31,11 +31,8 @@ #include #include #include -#include #include "scsi_priv.h" -static int fc_queue_work(struct Scsi_Host *, struct work_struct *); - /* * Redefine so that we can have same named attributes in the * sdev/starget/host objects. @@ -50,7 +47,7 @@ static const char *get_fc_##title##_name(enum table_type table_key) \ int i; \ char *name = NULL; \ \ - for (i = 0; i < ARRAY_SIZE(table); i++) { \ + for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \ if (table[i].value == table_key) { \ name = table[i].name; \ break; \ @@ -65,7 +62,7 @@ static int get_fc_##title##_match(const char *table_key, \ { \ int i; \ \ - for (i = 0; i < ARRAY_SIZE(table); i++) { \ + for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \ if (strncmp(table_key, table[i].name, \ table[i].matchlen) == 0) { \ *value = table[i].value; \ @@ -140,7 +137,7 @@ get_fc_##title##_names(u32 table_key, char *buf) \ ssize_t len = 0; \ int i; \ \ - for (i = 0; i < ARRAY_SIZE(table); i++) { \ + for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \ if (table[i].value & table_key) { \ len += sprintf(buf + len, "%s%s", \ prefix, table[i].name); \ @@ -215,8 +212,10 @@ fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names) #define FC_MGMTSRVR_PORTID 0x00000a +static void fc_shost_remove_rports(void *data); static void fc_timeout_deleted_rport(void *data); static void fc_scsi_scan_rport(void *data); +static void fc_rport_terminate(struct fc_rport *rport); /* * Attribute counts pre object type... @@ -288,58 +287,42 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, struct class_device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); - struct fc_host_attrs *fc_host = shost_to_fc_host(shost); /* * Set default values easily detected by the midlayer as * failure cases. The scsi lldd is responsible for initializing * all transport attributes to valid values per host. */ - fc_host->node_name = -1; - fc_host->port_name = -1; - fc_host->permanent_port_name = -1; - fc_host->supported_classes = FC_COS_UNSPECIFIED; - memset(fc_host->supported_fc4s, 0, - sizeof(fc_host->supported_fc4s)); - memset(fc_host->symbolic_name, 0, - sizeof(fc_host->symbolic_name)); - fc_host->supported_speeds = FC_PORTSPEED_UNKNOWN; - fc_host->maxframe_size = -1; - memset(fc_host->serial_number, 0, - sizeof(fc_host->serial_number)); - - fc_host->port_id = -1; - fc_host->port_type = FC_PORTTYPE_UNKNOWN; - fc_host->port_state = FC_PORTSTATE_UNKNOWN; - memset(fc_host->active_fc4s, 0, - sizeof(fc_host->active_fc4s)); - fc_host->speed = FC_PORTSPEED_UNKNOWN; - fc_host->fabric_name = -1; - - fc_host->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN; - - INIT_LIST_HEAD(&fc_host->rports); - INIT_LIST_HEAD(&fc_host->rport_bindings); - fc_host->next_rport_number = 0; - fc_host->next_target_id = 0; - - snprintf(fc_host->work_q_name, KOBJ_NAME_LEN, "fc_wq_%d", - shost->host_no); - fc_host->work_q = create_singlethread_workqueue( - fc_host->work_q_name); - if (!fc_host->work_q) - return -ENOMEM; - - snprintf(fc_host->devloss_work_q_name, KOBJ_NAME_LEN, "fc_dl_%d", - shost->host_no); - fc_host->devloss_work_q = create_singlethread_workqueue( - fc_host->devloss_work_q_name); - if (!fc_host->devloss_work_q) { - destroy_workqueue(fc_host->work_q); - fc_host->work_q = NULL; - return -ENOMEM; - } - + fc_host_node_name(shost) = -1; + fc_host_port_name(shost) = -1; + fc_host_permanent_port_name(shost) = -1; + fc_host_supported_classes(shost) = FC_COS_UNSPECIFIED; + memset(fc_host_supported_fc4s(shost), 0, + sizeof(fc_host_supported_fc4s(shost))); + memset(fc_host_symbolic_name(shost), 0, + sizeof(fc_host_symbolic_name(shost))); + fc_host_supported_speeds(shost) = FC_PORTSPEED_UNKNOWN; + fc_host_maxframe_size(shost) = -1; + memset(fc_host_serial_number(shost), 0, + sizeof(fc_host_serial_number(shost))); + + fc_host_port_id(shost) = -1; + fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; + fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; + memset(fc_host_active_fc4s(shost), 0, + sizeof(fc_host_active_fc4s(shost))); + fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; + fc_host_fabric_name(shost) = -1; + + fc_host_tgtid_bind_type(shost) = FC_TGTID_BIND_BY_WWPN; + + INIT_LIST_HEAD(&fc_host_rports(shost)); + INIT_LIST_HEAD(&fc_host_rport_bindings(shost)); + fc_host_next_rport_number(shost) = 0; + fc_host_next_target_id(shost) = 0; + + fc_host_flags(shost) = 0; + INIT_WORK(&fc_host_rport_del_work(shost), fc_shost_remove_rports, shost); return 0; } @@ -368,7 +351,7 @@ static DECLARE_TRANSPORT_CLASS(fc_rport_class, * should insulate the loss of a remote port. * The maximum will be capped by the value of SCSI_DEVICE_BLOCK_MAX_TIMEOUT. */ -static unsigned int fc_dev_loss_tmo = 60; /* seconds */ +static unsigned int fc_dev_loss_tmo = SCSI_DEVICE_BLOCK_MAX_TIMEOUT; module_param_named(dev_loss_tmo, fc_dev_loss_tmo, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(dev_loss_tmo, @@ -895,9 +878,9 @@ store_fc_private_host_tgtid_bind_type(struct class_device *cdev, while (!list_empty(&fc_host_rport_bindings(shost))) { get_list_head_entry(rport, &fc_host_rport_bindings(shost), peers); - list_del(&rport->peers); - rport->port_state = FC_PORTSTATE_DELETED; - fc_queue_work(shost, &rport->rport_delete_work); + spin_unlock_irqrestore(shost->host_lock, flags); + fc_rport_terminate(rport); + spin_lock_irqsave(shost->host_lock, flags); } spin_unlock_irqrestore(shost->host_lock, flags); } @@ -1107,40 +1090,6 @@ static int fc_rport_match(struct attribute_container *cont, } -/** - * fc_timed_out - FC Transport I/O timeout intercept handler - * - * @scmd: The SCSI command which timed out - * - * This routine protects against error handlers getting invoked while a - * rport is in a blocked state, typically due to a temporarily loss of - * connectivity. If the error handlers are allowed to proceed, requests - * to abort i/o, reset the target, etc will likely fail as there is no way - * to communicate with the device to perform the requested function. These - * failures may result in the midlayer taking the device offline, requiring - * manual intervention to restore operation. - * - * This routine, called whenever an i/o times out, validates the state of - * the underlying rport. If the rport is blocked, it returns - * EH_RESET_TIMER, which will continue to reschedule the timeout. - * Eventually, either the device will return, or devloss_tmo will fire, - * and when the timeout then fires, it will be handled normally. - * If the rport is not blocked, normal error handling continues. - * - * Notes: - * This routine assumes no locks are held on entry. - **/ -static enum scsi_eh_timer_return -fc_timed_out(struct scsi_cmnd *scmd) -{ - struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device)); - - if (rport->port_state == FC_PORTSTATE_BLOCKED) - return EH_RESET_TIMER; - - return EH_NOT_HANDLED; -} - /* * Must be called with shost->host_lock held */ @@ -1166,13 +1115,15 @@ static int fc_user_scan(struct Scsi_Host *shost, uint channel, struct scsi_transport_template * fc_attach_transport(struct fc_function_template *ft) { - int count; - struct fc_internal *i = kzalloc(sizeof(struct fc_internal), + struct fc_internal *i = kmalloc(sizeof(struct fc_internal), GFP_KERNEL); + int count; if (unlikely(!i)) return NULL; + memset(i, 0, sizeof(struct fc_internal)); + i->t.target_attrs.ac.attrs = &i->starget_attrs[0]; i->t.target_attrs.ac.class = &fc_transport_class.class; i->t.target_attrs.ac.match = fc_target_match; @@ -1197,8 +1148,6 @@ fc_attach_transport(struct fc_function_template *ft) /* Transport uses the shost workq for scsi scanning */ i->t.create_work_queue = 1; - i->t.eh_timed_out = fc_timed_out; - i->t.user_scan = fc_user_scan; /* @@ -1278,92 +1227,6 @@ void fc_release_transport(struct scsi_transport_template *t) } EXPORT_SYMBOL(fc_release_transport); -/** - * fc_queue_work - Queue work to the fc_host workqueue. - * @shost: Pointer to Scsi_Host bound to fc_host. - * @work: Work to queue for execution. - * - * Return value: - * 1 - work queued for execution - * 0 - work is already queued - * -EINVAL - work queue doesn't exist - **/ -static int -fc_queue_work(struct Scsi_Host *shost, struct work_struct *work) -{ - if (unlikely(!fc_host_work_q(shost))) { - printk(KERN_ERR - "ERROR: FC host '%s' attempted to queue work, " - "when no workqueue created.\n", shost->hostt->name); - dump_stack(); - - return -EINVAL; - } - - return queue_work(fc_host_work_q(shost), work); -} - -/** - * fc_flush_work - Flush a fc_host's workqueue. - * @shost: Pointer to Scsi_Host bound to fc_host. - **/ -static void -fc_flush_work(struct Scsi_Host *shost) -{ - if (!fc_host_work_q(shost)) { - printk(KERN_ERR - "ERROR: FC host '%s' attempted to flush work, " - "when no workqueue created.\n", shost->hostt->name); - dump_stack(); - return; - } - - flush_workqueue(fc_host_work_q(shost)); -} - -/** - * fc_queue_devloss_work - Schedule work for the fc_host devloss workqueue. - * @shost: Pointer to Scsi_Host bound to fc_host. - * @work: Work to queue for execution. - * @delay: jiffies to delay the work queuing - * - * Return value: - * 0 on success / != 0 for error - **/ -static int -fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work, - unsigned long delay) -{ - if (unlikely(!fc_host_devloss_work_q(shost))) { - printk(KERN_ERR - "ERROR: FC host '%s' attempted to queue work, " - "when no workqueue created.\n", shost->hostt->name); - dump_stack(); - - return -EINVAL; - } - - return queue_delayed_work(fc_host_devloss_work_q(shost), work, delay); -} - -/** - * fc_flush_devloss - Flush a fc_host's devloss workqueue. - * @shost: Pointer to Scsi_Host bound to fc_host. - **/ -static void -fc_flush_devloss(struct Scsi_Host *shost) -{ - if (!fc_host_devloss_work_q(shost)) { - printk(KERN_ERR - "ERROR: FC host '%s' attempted to flush work, " - "when no workqueue created.\n", shost->hostt->name); - dump_stack(); - return; - } - - flush_workqueue(fc_host_devloss_work_q(shost)); -} - /** * fc_remove_host - called to terminate any fc_transport-related elements @@ -1385,102 +1248,36 @@ void fc_remove_host(struct Scsi_Host *shost) { struct fc_rport *rport, *next_rport; - struct workqueue_struct *work_q; - struct fc_host_attrs *fc_host = shost_to_fc_host(shost); /* Remove any remote ports */ list_for_each_entry_safe(rport, next_rport, - &fc_host->rports, peers) { - list_del(&rport->peers); - rport->port_state = FC_PORTSTATE_DELETED; - fc_queue_work(shost, &rport->rport_delete_work); - } - + &fc_host_rports(shost), peers) + fc_rport_terminate(rport); list_for_each_entry_safe(rport, next_rport, - &fc_host->rport_bindings, peers) { - list_del(&rport->peers); - rport->port_state = FC_PORTSTATE_DELETED; - fc_queue_work(shost, &rport->rport_delete_work); - } - - /* flush all scan work items */ - scsi_flush_work(shost); - - /* flush all stgt delete, and rport delete work items, then kill it */ - if (fc_host->work_q) { - work_q = fc_host->work_q; - fc_host->work_q = NULL; - destroy_workqueue(work_q); - } - - /* flush all devloss work items, then kill it */ - if (fc_host->devloss_work_q) { - work_q = fc_host->devloss_work_q; - fc_host->devloss_work_q = NULL; - destroy_workqueue(work_q); - } + &fc_host_rport_bindings(shost), peers) + fc_rport_terminate(rport); } EXPORT_SYMBOL(fc_remove_host); - -/** - * fc_starget_delete - called to delete the scsi decendents of an rport - * (target and all sdevs) - * - * @data: remote port to be operated on. - **/ -static void -fc_starget_delete(void *data) -{ - struct fc_rport *rport = (struct fc_rport *)data; - struct Scsi_Host *shost = rport_to_shost(rport); - unsigned long flags; - - spin_lock_irqsave(shost->host_lock, flags); - if (rport->flags & FC_RPORT_DEVLOSS_PENDING) { - spin_unlock_irqrestore(shost->host_lock, flags); - if (!cancel_delayed_work(&rport->dev_loss_work)) - fc_flush_devloss(shost); - spin_lock_irqsave(shost->host_lock, flags); - rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; - } - spin_unlock_irqrestore(shost->host_lock, flags); - - scsi_remove_target(&rport->dev); -} - - -/** - * fc_rport_final_delete - finish rport termination and delete it. - * - * @data: remote port to be deleted. - **/ +/* + * fc_rport_tgt_remove - Removes the scsi target on the remote port + * @rport: The remote port to be operated on + */ static void -fc_rport_final_delete(void *data) +fc_rport_tgt_remove(struct fc_rport *rport) { - struct fc_rport *rport = (struct fc_rport *)data; - struct device *dev = &rport->dev; struct Scsi_Host *shost = rport_to_shost(rport); - /* Delete SCSI target and sdevs */ - if (rport->scsi_target_id != -1) - fc_starget_delete(data); + scsi_target_unblock(&rport->dev); - /* - * if a scan is pending, flush the SCSI Host work_q so that - * that we can reclaim the rport scan work element. - */ - if (rport->flags & FC_RPORT_SCAN_PENDING) - scsi_flush_work(shost); + /* Stop anything on the workq */ + if (!cancel_delayed_work(&rport->dev_loss_work)) + flush_scheduled_work(); + scsi_flush_work(shost); - transport_remove_device(dev); - device_del(dev); - transport_destroy_device(dev); - put_device(&shost->shost_gendev); /* for fc_host->rport list */ - put_device(dev); /* for self-reference */ + scsi_remove_target(&rport->dev); } - /** * fc_rport_create - allocates and creates a remote FC port. * @shost: scsi host the remote port is connected to. @@ -1498,7 +1295,8 @@ struct fc_rport * fc_rport_create(struct Scsi_Host *shost, int channel, struct fc_rport_identifiers *ids) { - struct fc_host_attrs *fc_host = shost_to_fc_host(shost); + struct fc_host_attrs *fc_host = + (struct fc_host_attrs *)shost->shost_data; struct fc_internal *fci = to_fc_internal(shost->transportt); struct fc_rport *rport; struct device *dev; @@ -1507,11 +1305,12 @@ fc_rport_create(struct Scsi_Host *shost, int channel, size_t size; size = (sizeof(struct fc_rport) + fci->f->dd_fcrport_size); - rport = kzalloc(size, GFP_KERNEL); + rport = kmalloc(size, GFP_KERNEL); if (unlikely(!rport)) { printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); return NULL; } + memset(rport, 0, size); rport->maxframe_size = -1; rport->supported_classes = FC_COS_UNSPECIFIED; @@ -1527,8 +1326,6 @@ fc_rport_create(struct Scsi_Host *shost, int channel, INIT_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport, rport); INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport); - INIT_WORK(&rport->stgt_delete_work, fc_starget_delete, rport); - INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete, rport); spin_lock_irqsave(shost->host_lock, flags); @@ -1537,14 +1334,14 @@ fc_rport_create(struct Scsi_Host *shost, int channel, rport->scsi_target_id = fc_host->next_target_id++; else rport->scsi_target_id = -1; - list_add_tail(&rport->peers, &fc_host->rports); - get_device(&shost->shost_gendev); /* for fc_host->rport list */ + list_add_tail(&rport->peers, &fc_host_rports(shost)); + get_device(&shost->shost_gendev); spin_unlock_irqrestore(shost->host_lock, flags); dev = &rport->dev; - device_initialize(dev); /* takes self reference */ - dev->parent = get_device(&shost->shost_gendev); /* parent reference */ + device_initialize(dev); + dev->parent = get_device(&shost->shost_gendev); dev->release = fc_rport_dev_release; sprintf(dev->bus_id, "rport-%d:%d-%d", shost->host_no, channel, rport->number); @@ -1558,19 +1355,18 @@ fc_rport_create(struct Scsi_Host *shost, int channel, transport_add_device(dev); transport_configure_device(dev); - if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) { + if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) /* initiate a scan of the target */ - rport->flags |= FC_RPORT_SCAN_PENDING; scsi_queue_work(shost, &rport->scan_work); - } return rport; delete_rport: transport_destroy_device(dev); + put_device(dev->parent); spin_lock_irqsave(shost->host_lock, flags); list_del(&rport->peers); - put_device(&shost->shost_gendev); /* for fc_host->rport list */ + put_device(&shost->shost_gendev); spin_unlock_irqrestore(shost->host_lock, flags); put_device(dev->parent); kfree(rport); @@ -1621,14 +1417,10 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, struct fc_rport_identifiers *ids) { struct fc_internal *fci = to_fc_internal(shost->transportt); - struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_rport *rport; unsigned long flags; int match = 0; - /* ensure any stgt delete functions are done */ - fc_flush_work(shost); - /* * Search the list of "active" rports, for an rport that has been * deleted, but we've held off the real delete while the target @@ -1636,12 +1428,12 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, */ spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry(rport, &fc_host->rports, peers) { + list_for_each_entry(rport, &fc_host_rports(shost), peers) { if ((rport->port_state == FC_PORTSTATE_BLOCKED) && (rport->channel == channel)) { - switch (fc_host->tgtid_bind_type) { + switch (fc_host_tgtid_bind_type(shost)) { case FC_TGTID_BIND_BY_WWPN: case FC_TGTID_BIND_NONE: if (rport->port_name == ids->port_name) @@ -1695,36 +1487,27 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, * transaction. */ if (!cancel_delayed_work(work)) - fc_flush_devloss(shost); - - spin_lock_irqsave(shost->host_lock, flags); - - rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; + flush_scheduled_work(); /* initiate a scan of the target */ - rport->flags |= FC_RPORT_SCAN_PENDING; scsi_queue_work(shost, &rport->scan_work); - spin_unlock_irqrestore(shost->host_lock, flags); - - scsi_target_unblock(&rport->dev); - return rport; } } } /* Search the bindings array */ - if (fc_host->tgtid_bind_type != FC_TGTID_BIND_NONE) { + if (fc_host_tgtid_bind_type(shost) != FC_TGTID_BIND_NONE) { /* search for a matching consistent binding */ - list_for_each_entry(rport, &fc_host->rport_bindings, + list_for_each_entry(rport, &fc_host_rport_bindings(shost), peers) { if (rport->channel != channel) continue; - switch (fc_host->tgtid_bind_type) { + switch (fc_host_tgtid_bind_type(shost)) { case FC_TGTID_BIND_BY_WWPN: if (rport->port_name == ids->port_name) match = 1; @@ -1742,7 +1525,8 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, } if (match) { - list_move_tail(&rport->peers, &fc_host->rports); + list_move_tail(&rport->peers, + &fc_host_rports(shost)); break; } } @@ -1756,18 +1540,15 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, rport->roles = ids->roles; rport->port_state = FC_PORTSTATE_ONLINE; + spin_unlock_irqrestore(shost->host_lock, flags); + if (fci->f->dd_fcrport_size) memset(rport->dd_data, 0, fci->f->dd_fcrport_size); - if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) { + if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) /* initiate a scan of the target */ - rport->flags |= FC_RPORT_SCAN_PENDING; scsi_queue_work(shost, &rport->scan_work); - spin_unlock_irqrestore(shost->host_lock, flags); - scsi_target_unblock(&rport->dev); - } else - spin_unlock_irqrestore(shost->host_lock, flags); return rport; } @@ -1782,6 +1563,30 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, } EXPORT_SYMBOL(fc_remote_port_add); +/* + * fc_rport_terminate - this routine tears down and deallocates a remote port. + * @rport: The remote port to be terminated + * + * Notes: + * This routine assumes no locks are held on entry. + */ +static void +fc_rport_terminate(struct fc_rport *rport) +{ + struct Scsi_Host *shost = rport_to_shost(rport); + struct device *dev = &rport->dev; + unsigned long flags; + + fc_rport_tgt_remove(rport); + + transport_remove_device(dev); + device_del(dev); + transport_destroy_device(dev); + spin_lock_irqsave(shost->host_lock, flags); + list_del(&rport->peers); + spin_unlock_irqrestore(shost->host_lock, flags); + put_device(&shost->shost_gendev); +} /** * fc_remote_port_delete - notifies the fc transport that a remote @@ -1836,39 +1641,20 @@ EXPORT_SYMBOL(fc_remote_port_add); void fc_remote_port_delete(struct fc_rport *rport) { - struct Scsi_Host *shost = rport_to_shost(rport); int timeout = rport->dev_loss_tmo; - unsigned long flags; - - /* - * No need to flush the fc_host work_q's, as all adds are synchronous. - * - * We do need to reclaim the rport scan work element, so eventually - * (in fc_rport_final_delete()) we'll flush the scsi host work_q if - * there's still a scan pending. - */ - - spin_lock_irqsave(shost->host_lock, flags); /* If no scsi target id mapping, delete it */ if (rport->scsi_target_id == -1) { - list_del(&rport->peers); - rport->port_state = FC_PORTSTATE_DELETED; - fc_queue_work(shost, &rport->rport_delete_work); - spin_unlock_irqrestore(shost->host_lock, flags); + fc_rport_terminate(rport); return; } - rport->port_state = FC_PORTSTATE_BLOCKED; - - rport->flags |= FC_RPORT_DEVLOSS_PENDING; - - spin_unlock_irqrestore(shost->host_lock, flags); - scsi_target_block(&rport->dev); /* cap the length the devices can be blocked until they are deleted */ - fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ); + schedule_delayed_work(&rport->dev_loss_work, timeout * HZ); + + rport->port_state = FC_PORTSTATE_BLOCKED; } EXPORT_SYMBOL(fc_remote_port_delete); @@ -1896,7 +1682,8 @@ void fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) { struct Scsi_Host *shost = rport_to_shost(rport); - struct fc_host_attrs *fc_host = shost_to_fc_host(shost); + struct fc_host_attrs *fc_host = + (struct fc_host_attrs *)shost->shost_data; unsigned long flags; int create = 0; @@ -1908,11 +1695,10 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) } else if (!(rport->roles & FC_RPORT_ROLE_FCP_TARGET)) create = 1; } + spin_unlock_irqrestore(shost->host_lock, flags); rport->roles = roles; - spin_unlock_irqrestore(shost->host_lock, flags); - if (create) { /* * There may have been a delete timer running on the @@ -1927,21 +1713,10 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) * transaction. */ if (!cancel_delayed_work(&rport->dev_loss_work)) - fc_flush_devloss(shost); - - spin_lock_irqsave(shost->host_lock, flags); - rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; - spin_unlock_irqrestore(shost->host_lock, flags); - - /* ensure any stgt delete functions are done */ - fc_flush_work(shost); + flush_scheduled_work(); /* initiate a scan of the target */ - spin_lock_irqsave(shost->host_lock, flags); - rport->flags |= FC_RPORT_SCAN_PENDING; scsi_queue_work(shost, &rport->scan_work); - spin_unlock_irqrestore(shost->host_lock, flags); - scsi_target_unblock(&rport->dev); } } EXPORT_SYMBOL(fc_remote_port_rolechg); @@ -1958,25 +1733,22 @@ fc_timeout_deleted_rport(void *data) { struct fc_rport *rport = (struct fc_rport *)data; struct Scsi_Host *shost = rport_to_shost(rport); - struct fc_host_attrs *fc_host = shost_to_fc_host(shost); unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); - rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; - /* - * If the port is ONLINE, then it came back. Validate it's still an - * FCP target. If not, tear down the scsi_target on it. + * If the port is ONLINE, then it came back, but was no longer an + * FCP target. Thus we need to tear down the scsi_target on it. */ - if ((rport->port_state == FC_PORTSTATE_ONLINE) && - !(rport->roles & FC_RPORT_ROLE_FCP_TARGET)) { - dev_printk(KERN_ERR, &rport->dev, - "blocked FC remote port time out: no longer" - " a FCP target, removing starget\n"); + if (rport->port_state == FC_PORTSTATE_ONLINE) { spin_unlock_irqrestore(shost->host_lock, flags); - scsi_target_unblock(&rport->dev); - fc_queue_work(shost, &rport->stgt_delete_work); + + dev_printk(KERN_ERR, &rport->dev, + "blocked FC remote port time out: removing target\n"); + + fc_rport_tgt_remove(rport); + return; } @@ -1987,13 +1759,11 @@ fc_timeout_deleted_rport(void *data) return; } - if (fc_host->tgtid_bind_type == FC_TGTID_BIND_NONE) { - list_del(&rport->peers); - rport->port_state = FC_PORTSTATE_DELETED; + if (fc_host_tgtid_bind_type(shost) == FC_TGTID_BIND_NONE) { + spin_unlock_irqrestore(shost->host_lock, flags); dev_printk(KERN_ERR, &rport->dev, "blocked FC remote port time out: removing target\n"); - fc_queue_work(shost, &rport->rport_delete_work); - spin_unlock_irqrestore(shost->host_lock, flags); + fc_rport_terminate(rport); return; } @@ -2001,7 +1771,7 @@ fc_timeout_deleted_rport(void *data) "blocked FC remote port time out: removing target and " "saving binding\n"); - list_move_tail(&rport->peers, &fc_host->rport_bindings); + list_move_tail(&rport->peers, &fc_host_rport_bindings(shost)); /* * Note: We do not remove or clear the hostdata area. This allows @@ -2015,10 +1785,10 @@ fc_timeout_deleted_rport(void *data) rport->maxframe_size = -1; rport->supported_classes = FC_COS_UNSPECIFIED; rport->roles = FC_RPORT_ROLE_UNKNOWN; - rport->port_state = FC_PORTSTATE_NOTPRESENT; + rport->port_state = FC_PORTSTATE_DELETED; /* remove the identifiers that aren't used in the consisting binding */ - switch (fc_host->tgtid_bind_type) { + switch (fc_host_tgtid_bind_type(shost)) { case FC_TGTID_BIND_BY_WWPN: rport->node_name = -1; rport->port_id = -1; @@ -2039,33 +1809,71 @@ fc_timeout_deleted_rport(void *data) * As this only occurs if the remote port (scsi target) * went away and didn't come back - we'll remove * all attached scsi devices. + * + * We'll schedule the shost work item to perform the actual removal + * to avoid recursion in the different flush calls if we perform + * the removal in each target - and there are lots of targets + * whose timeouts fire at the same time. */ - spin_unlock_irqrestore(shost->host_lock, flags); - scsi_target_unblock(&rport->dev); - fc_queue_work(shost, &rport->stgt_delete_work); + if ( !(fc_host_flags(shost) & FC_SHOST_RPORT_DEL_SCHEDULED)) { + fc_host_flags(shost) |= FC_SHOST_RPORT_DEL_SCHEDULED; + scsi_queue_work(shost, &fc_host_rport_del_work(shost)); + } + + spin_unlock_irqrestore(shost->host_lock, flags); } /** * fc_scsi_scan_rport - called to perform a scsi scan on a remote port. * + * Will unblock the target (in case it went away and has now come back), + * then invoke a scan. + * * @data: remote port to be scanned. **/ static void fc_scsi_scan_rport(void *data) { struct fc_rport *rport = (struct fc_rport *)data; - struct Scsi_Host *shost = rport_to_shost(rport); - unsigned long flags; - if ((rport->port_state == FC_PORTSTATE_ONLINE) && - (rport->roles & FC_RPORT_ROLE_FCP_TARGET)) { - scsi_scan_target(&rport->dev, rport->channel, - rport->scsi_target_id, SCAN_WILD_CARD, 1); - } + scsi_target_unblock(&rport->dev); + scsi_scan_target(&rport->dev, rport->channel, rport->scsi_target_id, + SCAN_WILD_CARD, 1); +} + + +/** + * fc_shost_remove_rports - called to remove all rports that are marked + * as in a deleted (not connected) state. + * + * @data: shost whose rports are to be looked at + **/ +static void +fc_shost_remove_rports(void *data) +{ + struct Scsi_Host *shost = (struct Scsi_Host *)data; + struct fc_rport *rport, *next_rport; + unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); - rport->flags &= ~FC_RPORT_SCAN_PENDING; + while (fc_host_flags(shost) & FC_SHOST_RPORT_DEL_SCHEDULED) { + + fc_host_flags(shost) &= ~FC_SHOST_RPORT_DEL_SCHEDULED; + +restart_search: + list_for_each_entry_safe(rport, next_rport, + &fc_host_rport_bindings(shost), peers) { + if (rport->port_state == FC_PORTSTATE_DELETED) { + rport->port_state = FC_PORTSTATE_NOTPRESENT; + spin_unlock_irqrestore(shost->host_lock, flags); + fc_rport_tgt_remove(rport); + spin_lock_irqsave(shost->host_lock, flags); + goto restart_search; + } + } + + } spin_unlock_irqrestore(shost->host_lock, flags); }