From: Mike Christie <[EMAIL PROTECTED]> This is a first stab at removing the libfc dummy rport code and private remote port code in other drivers. I am just sending the fc class code to make sure I have the basic idea right.
JamesS had presented two possible solutions for how to deal with rports that exist and how to resolve dd_data state: http://www.open-fcoe.org/pipermail/devel/2008-July/000401.html In one proposal we could have the LLD call some fc class helpers to find existing rports by wwpn/wwnn. And the alternative, is to try and make fc_remote_port_add figure things out and call back into the LLD. I went with the former, because some of the issues like how to handle if the class finds a rport with a matching port_id that is not in the blocked state gets complicated from a callback. This patch was made over scsi-misc, becuase there are some patches in there not in the fcoe tree. You can pull his tree into the fcoe one and apply the patch. Signed-off-by: Mike Christie <[EMAIL PROTECTED]> --- drivers/scsi/scsi_transport_fc.c | 435 ++++++++++++++++++++++---------------- include/scsi/scsi_transport_fc.h | 12 +- 2 files changed, 263 insertions(+), 184 deletions(-) diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index c276d11..a1311b2 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -144,6 +144,7 @@ static struct { { FC_PORTSTATE_ERROR, "Error" }, { FC_PORTSTATE_LOOPBACK, "Loopback" }, { FC_PORTSTATE_DELETED, "Deleted" }, + { FC_PORTSTATE_ROGUE, "Rogue" }, }; fc_enum_name_search(port_state, fc_port_state, fc_port_state_names) #define FC_PORTSTATE_MAX_NAMELEN 20 @@ -387,6 +388,7 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, INIT_LIST_HEAD(&fc_host->rports); INIT_LIST_HEAD(&fc_host->rport_bindings); + INIT_LIST_HEAD(&fc_host->rogue_rports); INIT_LIST_HEAD(&fc_host->vports); fc_host->next_rport_number = 0; fc_host->next_target_id = 0; @@ -1518,7 +1520,7 @@ store_fc_private_host_tgtid_bind_type(struct device *dev, while (!list_empty(&fc_host_rport_bindings(shost))) { get_list_head_entry(rport, &fc_host_rport_bindings(shost), peers); - list_del(&rport->peers); + list_del_init(&rport->peers); rport->port_state = FC_PORTSTATE_DELETED; fc_queue_work(shost, &rport->rport_delete_work); } @@ -2294,14 +2296,14 @@ fc_remove_host(struct Scsi_Host *shost) /* Remove any remote ports */ list_for_each_entry_safe(rport, next_rport, &fc_host->rports, peers) { - list_del(&rport->peers); + list_del_init(&rport->peers); rport->port_state = FC_PORTSTATE_DELETED; fc_queue_work(shost, &rport->rport_delete_work); } list_for_each_entry_safe(rport, next_rport, &fc_host->rport_bindings, peers) { - list_del(&rport->peers); + list_del_init(&rport->peers); rport->port_state = FC_PORTSTATE_DELETED; fc_queue_work(shost, &rport->rport_delete_work); } @@ -2414,51 +2416,41 @@ fc_rport_final_delete(struct work_struct *work) 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 */ + fc_remote_port_free(rport); } - /** - * fc_rport_create - allocates and creates a remote FC port. + * fc_remote_port_alloc - allocates a remote FC port. * @shost: scsi host the remote port is connected to. * @channel: Channel on shost port connected to. - * @ids: The world wide names, fc address, and FC4 port - * roles for the remote port. * - * Allocates and creates the remoter port structure, including the - * class and sysfs creation. + * Allocates the remoter port structure. It has no binding + * and no sysfs representation at this time. It is just used to send + * discovery requests through. * * Notes: * This routine assumes no locks are held on entry. */ -static struct fc_rport * -fc_rport_create(struct Scsi_Host *shost, int channel, - struct fc_rport_identifiers *ids) +struct fc_rport *fc_remote_port_alloc(struct Scsi_Host *shost, int channel) { struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_internal *fci = to_fc_internal(shost->transportt); struct fc_rport *rport; struct device *dev; unsigned long flags; - int error; - size_t size; - size = (sizeof(struct fc_rport) + fci->f->dd_fcrport_size); - rport = kzalloc(size, GFP_KERNEL); - if (unlikely(!rport)) { + rport = kzalloc(sizeof(struct fc_rport) + fci->f->dd_fcrport_size, + GFP_KERNEL); + if (!rport) { printk(KERN_ERR "%s: allocation failure\n", __func__); return NULL; } rport->maxframe_size = -1; rport->supported_classes = FC_COS_UNSPECIFIED; + rport->roles = FC_PORT_ROLE_UNKNOWN; rport->dev_loss_tmo = fc_dev_loss_tmo; - memcpy(&rport->node_name, &ids->node_name, sizeof(rport->node_name)); - memcpy(&rport->port_name, &ids->port_name, sizeof(rport->port_name)); - rport->port_id = ids->port_id; - rport->roles = ids->roles; - rport->port_state = FC_PORTSTATE_ONLINE; + rport->port_state = FC_PORTSTATE_ROGUE; if (fci->f->dd_fcrport_size) rport->dd_data = &rport[1]; rport->channel = channel; @@ -2470,59 +2462,181 @@ fc_rport_create(struct Scsi_Host *shost, int channel, INIT_WORK(&rport->stgt_delete_work, fc_starget_delete); INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete); + dev = &rport->dev; + dev->parent = get_device(&shost->shost_gendev); /* parent reference */ + dev->release = fc_rport_dev_release; + sprintf(dev->bus_id, "rport-%d:%d-%d", + shost->host_no, channel, rport->number); + device_initialize(dev); /* takes self reference */ + + spin_lock_irqsave(shost->host_lock, flags); + rport->number = fc_host->next_rport_number++; + list_add_tail(&rport->peers, &fc_host->rogue_rports); + get_device(&shost->shost_gendev); /* for fc_host->*rports list */ + spin_unlock_irqrestore(shost->host_lock, flags); + return rport; +} +EXPORT_SYMBOL_GPL(fc_remote_port_alloc); + +/** + * fc_remote_port_free - drop reference from allocation + * @rport: remote port + * + * This should be called if the LLD has not called + * add on the rport and wishes to free the resources for the rogue port. + * For example if discovery failed, then the LLD should call this + * function to free the rport that was allocated. + */ +void fc_remote_port_free(struct fc_rport *rport) +{ + struct Scsi_Host *shost = rport_to_shost(rport); + unsigned long flags; + + spin_lock_irqsave(shost->host_lock, flags); + if (!list_empty(&rport->peers)) + list_del(&rport->peers); + /* for fc_host->rogue_rports list */ + put_device(&rport->dev); + spin_unlock_irqrestore(shost->host_lock, flags); + + /* for self-reference */ + put_device(&rport->dev); +} +EXPORT_SYMBOL_GPL(fc_remote_port_free); + +static struct fc_rport * +rport_lookup(struct fc_host_attrs *fc_host, struct list_head *list, int channel, + enum fc_tgtid_binding_type tgtid_bind_type, + struct fc_rport_identifiers *ids) +{ + struct fc_rport *rport; + + list_for_each_entry(rport, list, peers) { + if (rport->channel != channel) + continue; + + switch (tgtid_bind_type) { + case FC_TGTID_BIND_BY_WWPN: + case FC_TGTID_BIND_NONE: + if (rport->port_name != ids->port_name) + continue; + break; + case FC_TGTID_BIND_BY_WWNN: + if (rport->node_name != ids->node_name) + continue; + break; + case FC_TGTID_BIND_BY_ID: + if (rport->port_id != ids->port_id) + continue; + break; + default: + continue; + } + get_device(&rport->dev); + return rport; + } + return NULL; +} + +/** + * fc_remote_port_lookup - lookup a remote port by id + * @shost: scsi host + * @channel: channel + * @tgtid_bind_type: bind type + * @id: port id + * + * If a the port is found, it will be returned with a refcount on it. + * The caller must do a put_device on the rport when it is done. + * This function will check all rports lists, so the caller must + * check the rport port_state before processing. + * + * Caller must hold host lock. + */ +static struct fc_rport * +__fc_remote_port_lookup(struct Scsi_Host *shost, int channel, + enum fc_tgtid_binding_type tgtid_bind_type, + struct fc_rport_identifiers *ids) +{ + struct fc_host_attrs *fc_host = shost_to_fc_host(shost); + struct fc_rport *rport; + + rport = rport_lookup(fc_host, &fc_host->rports, channel, + tgtid_bind_type, ids); + if (rport) + return rport; + + rport = rport_lookup(fc_host, &fc_host->rport_bindings, channel, + tgtid_bind_type, ids); + if (rport) + return rport; + + return rport_lookup(fc_host, &fc_host->rogue_rports, channel, + tgtid_bind_type, ids); +} + +struct fc_rport * +fc_remote_port_lookup(struct Scsi_Host *shost, int channel, + enum fc_tgtid_binding_type tgtid_bind_type, + struct fc_rport_identifiers *ids) +{ + struct fc_rport *rport; + unsigned long flags; + + spin_lock_irqsave(shost->host_lock, flags); + rport = __fc_remote_port_lookup(shost, channel, tgtid_bind_type, ids); + spin_unlock_irqrestore(shost->host_lock, flags); + return rport; +} +EXPORT_SYMBOL_GPL(fc_remote_port_lookup); + +static int remote_port_add(struct fc_rport *rport, + struct fc_rport_identifiers *ids) +{ + struct Scsi_Host *shost = rport_to_shost(rport); + struct fc_host_attrs *fc_host = shost_to_fc_host(shost); + struct device *dev; + unsigned long flags; + int error; + spin_lock_irqsave(shost->host_lock, flags); + memcpy(&rport->node_name, &ids->node_name, sizeof(rport->node_name)); + memcpy(&rport->port_name, &ids->port_name, sizeof(rport->port_name)); + rport->port_id = ids->port_id; + rport->roles = ids->roles; rport->number = fc_host->next_rport_number++; if (rport->roles & FC_PORT_ROLE_FCP_TARGET) 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_move_tail(&rport->peers, &fc_host->rports); 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 */ - dev->release = fc_rport_dev_release; - sprintf(dev->bus_id, "rport-%d:%d-%d", - shost->host_no, channel, rport->number); - transport_setup_device(dev); - error = device_add(dev); if (error) { printk(KERN_ERR "FC Remote Port device_add failed\n"); - goto delete_rport; + put_device(&shost->shost_gendev); + return error; } - transport_add_device(dev); - transport_configure_device(dev); + + transport_register_device(dev); if (rport->roles & FC_PORT_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); - spin_lock_irqsave(shost->host_lock, flags); - list_del(&rport->peers); - put_device(&shost->shost_gendev); /* for fc_host->rport list */ - spin_unlock_irqrestore(shost->host_lock, flags); - put_device(dev->parent); - kfree(rport); - return NULL; + return 0; } /** * fc_remote_port_add - notify fc transport of the existence of a remote FC port. - * @shost: scsi host the remote port is connected to. - * @channel: Channel on shost port connected to. - * @ids: The world wide names, fc address, and FC4 port - * roles for the remote port. + * @shost: scsi host the remote port is connected to. + * @rogue_rport: optional rogue rport. + * @channel: Channel on shost port connected to. + * @ids: The world wide names, fc address, and FC4 port + * roles for the remote port. * * The LLDD calls this routine to notify the transport of the existence * of a remote port. The LLDD provides the unique identifiers (wwpn,wwn) @@ -2552,157 +2666,102 @@ delete_rport: * * Should not be called from interrupt context. * + * The caller should have resolved rogue with active or deleted + * rports before calling. + * * Notes: * This routine assumes no locks are held on entry. */ struct fc_rport * fc_remote_port_add(struct Scsi_Host *shost, int channel, - struct fc_rport_identifiers *ids) + struct fc_rport *rogue_rport, + 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 - * is in a "blocked" state. - */ spin_lock_irqsave(shost->host_lock, flags); + rport = __fc_remote_port_lookup(shost, channel, + fc_host->tgtid_bind_type, ids); + if (rport) { + switch (rport->port_state) { + /* + * Found rport on the list of "active" rports that has been + * deleted, but we've held off the real delete while the target + * is in a "blocked" state. + */ + case FC_PORTSTATE_BLOCKED: + memcpy(&rport->node_name, &ids->node_name, + sizeof(rport->node_name)); + memcpy(&rport->port_name, &ids->port_name, + sizeof(rport->port_name)); + rport->port_id = ids->port_id; - list_for_each_entry(rport, &fc_host->rports, peers) { - - if ((rport->port_state == FC_PORTSTATE_BLOCKED) && - (rport->channel == channel)) { - - switch (fc_host->tgtid_bind_type) { - case FC_TGTID_BIND_BY_WWPN: - case FC_TGTID_BIND_NONE: - if (rport->port_name == ids->port_name) - match = 1; - break; - case FC_TGTID_BIND_BY_WWNN: - if (rport->node_name == ids->node_name) - match = 1; - break; - case FC_TGTID_BIND_BY_ID: - if (rport->port_id == ids->port_id) - match = 1; - break; - } - - if (match) { - - memcpy(&rport->node_name, &ids->node_name, - sizeof(rport->node_name)); - memcpy(&rport->port_name, &ids->port_name, - sizeof(rport->port_name)); - rport->port_id = ids->port_id; - - rport->port_state = FC_PORTSTATE_ONLINE; - rport->roles = ids->roles; - - spin_unlock_irqrestore(shost->host_lock, flags); - - if (fci->f->dd_fcrport_size) - memset(rport->dd_data, 0, - fci->f->dd_fcrport_size); + rport->port_state = FC_PORTSTATE_ONLINE; + rport->roles = ids->roles; - /* - * If we were not a target, cancel the - * io terminate and rport timers, and - * we're done. - * - * If we were a target, but our new role - * doesn't indicate a target, leave the - * timers running expecting the role to - * change as the target fully logs in. If - * it doesn't, the target will be torn down. - * - * If we were a target, and our role shows - * we're still a target, cancel the timers - * and kick off a scan. - */ - - /* was a target, not in roles */ - if ((rport->scsi_target_id != -1) && - (!(ids->roles & FC_PORT_ROLE_FCP_TARGET))) - return rport; - - /* - * Stop the fail io and dev_loss timers. - * If they flush, the port_state will - * be checked and will NOOP the function. - */ - if (!cancel_delayed_work(&rport->fail_io_work)) - fc_flush_devloss(shost); - if (!cancel_delayed_work(&rport->dev_loss_work)) - fc_flush_devloss(shost); - - spin_lock_irqsave(shost->host_lock, flags); - - rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT | - FC_RPORT_DEVLOSS_PENDING); - - /* if target, initiate a scan */ - if (rport->scsi_target_id != -1) { - 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); + spin_unlock_irqrestore(shost->host_lock, flags); + if (fci->f->dd_fcrport_size) + memset(rport->dd_data, 0, + fci->f->dd_fcrport_size); + + /* + * If we were not a target, cancel the + * io terminate and rport timers, and + * we're done. + * + * If we were a target, but our new role + * doesn't indicate a target, leave the + * timers running expecting the role to + * change as the target fully logs in. If + * it doesn't, the target will be torn down. + * + * If we were a target, and our role shows + * we're still a target, cancel the timers + * and kick off a scan. + */ + + /* was a target, not in roles */ + if ((rport->scsi_target_id != -1) && + (!(ids->roles & FC_PORT_ROLE_FCP_TARGET))) return rport; - } - } - } - /* - * Search the bindings array - * Note: if never a FCP target, you won't be on this list - */ - if (fc_host->tgtid_bind_type != FC_TGTID_BIND_NONE) { + /* + * Stop the fail io and dev_loss timers. + * If they flush, the port_state will + * be checked and will NOOP the function. + */ + if (!cancel_delayed_work(&rport->fail_io_work)) + fc_flush_devloss(shost); + if (!cancel_delayed_work(&rport->dev_loss_work)) + fc_flush_devloss(shost); - /* search for a matching consistent binding */ + spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry(rport, &fc_host->rport_bindings, - peers) { - if (rport->channel != channel) - continue; + rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT | + FC_RPORT_DEVLOSS_PENDING); - switch (fc_host->tgtid_bind_type) { - case FC_TGTID_BIND_BY_WWPN: - if (rport->port_name == ids->port_name) - match = 1; - break; - case FC_TGTID_BIND_BY_WWNN: - if (rport->node_name == ids->node_name) - match = 1; - break; - case FC_TGTID_BIND_BY_ID: - if (rport->port_id == ids->port_id) - match = 1; - break; - case FC_TGTID_BIND_NONE: /* to keep compiler happy */ - break; - } + /* if target, initiate a scan */ + if (rport->scsi_target_id != -1) { + 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); - if (match) { - list_move_tail(&rport->peers, &fc_host->rports); + return rport; + /* must have been on rport_bindings */ + case FC_PORTSTATE_NOTPRESENT: + if (fc_host->tgtid_bind_type == FC_TGTID_BIND_NONE) break; - } - } - if (match) { memcpy(&rport->node_name, &ids->node_name, sizeof(rport->node_name)); memcpy(&rport->port_name, &ids->port_name, @@ -2726,13 +2785,26 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, spin_unlock_irqrestore(shost->host_lock, flags); return rport; + /* new rport */ + default: + break; } } - spin_unlock_irqrestore(shost->host_lock, flags); /* No consistent binding found - create new remote port entry */ - rport = fc_rport_create(shost, channel, ids); + if (rogue_rport) + rport = rogue_rport; + else { + rport = fc_remote_port_alloc(shost, channel); + if (!rport) + return NULL; + } + + if (remote_port_add(rport, ids)) { + fc_remote_port_free(rport); + return NULL; + } return rport; } @@ -2980,7 +3052,7 @@ fc_timeout_deleted_rport(struct work_struct *work) if ((fc_host->tgtid_bind_type == FC_TGTID_BIND_NONE) || (rport->scsi_target_id == -1)) { - list_del(&rport->peers); + list_del_init(&rport->peers); rport->port_state = FC_PORTSTATE_DELETED; dev_printk(KERN_ERR, &rport->dev, "blocked FC remote port time out: removing" @@ -3167,7 +3239,6 @@ fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, goto delete_vport; } transport_add_device(dev); - transport_configure_device(dev); error = fci->f->vport_create(vport, ids->disable); if (error) { diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 49d8913..4d9a655 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -82,6 +82,7 @@ enum fc_port_state { FC_PORTSTATE_ERROR, FC_PORTSTATE_LOOPBACK, FC_PORTSTATE_DELETED, + FC_PORTSTATE_ROGUE, }; @@ -502,6 +503,7 @@ struct fc_host_attrs { /* internal data */ struct list_head rports; struct list_head rport_bindings; + struct list_head rogue_rports; struct list_head vports; u32 next_rport_number; u32 next_target_id; @@ -741,8 +743,14 @@ struct scsi_transport_template *fc_attach_transport( struct fc_function_template *); void fc_release_transport(struct scsi_transport_template *); void fc_remove_host(struct Scsi_Host *); -struct fc_rport *fc_remote_port_add(struct Scsi_Host *shost, - int channel, struct fc_rport_identifiers *ids); +struct fc_rport *fc_remote_port_lookup(struct Scsi_Host *shost, int channel, + enum fc_tgtid_binding_type tgtid_bind_type, + struct fc_rport_identifiers *ids); +struct fc_rport *fc_remote_port_alloc(struct Scsi_Host *shost, int channel); +void fc_remote_port_free(struct fc_rport *rport); +struct fc_rport *fc_remote_port_add(struct Scsi_Host *shost, int channel, + struct fc_rport *rogue_rport, + struct fc_rport_identifiers *ids); void fc_remote_port_delete(struct fc_rport *rport); void fc_remote_port_rolechg(struct fc_rport *rport, u32 roles); int scsi_is_fc_rport(const struct device *); -- 1.5.5.1 _______________________________________________ devel mailing list [email protected] http://www.open-fcoe.org/mailman/listinfo/devel
