Robert Love wrote:
> On Mon, 2009-08-10 at 12:23 -0700, Joe Eykholt wrote:
>> When an RSCN indicates changes to individual remote ports,
>> don't blindly log them out and then back in. Instead, determine
>> whether they're still in the directory, by doing GPN_ID.
>>
>> If that is successful, call login, which will send ADISC and reverify,
>> otherwise, call logoff. Perhaps we should just delete the rport,
>> not send LOGO, but it seems safer.
>>
>> Also, fix a possible issue where if a mix of records in the RSCN
>> cause us to queue disc_ports for disc_single and then we decide
>> to do full rediscovery, we leak memory for those disc_ports queued.
>>
>> So, go through the list of disc_ports even if doing full discovery.
>> Free the disc_ports in any case. If any of the disc_single() calls
>> return error, do a full discovery.
>>
>> The ability to fill in GPN_ID requests was added to fc_ct_fill().
>> For this, it needs the FC_ID to be passed in as an arg.
>> The did parameter for fc_elsct_send() is used for that, since the
>> actual D_DID will always be 0xfffffc for all CT requests so far.
>>
>> Signed-off-by: Joe Eykholt <[email protected]>
>> ---
>> drivers/scsi/libfc/fc_disc.c | 139
>> ++++++++++++++++++++++++++++++++++-------
>> drivers/scsi/libfc/fc_elsct.c | 2 -
>> include/scsi/fc_encode.h | 16 ++++-
>> 3 files changed, 130 insertions(+), 27 deletions(-)
>>
>>
>> diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
>> index 1a699f4..f694394 100644
>> --- a/drivers/scsi/libfc/fc_disc.c
>> +++ b/drivers/scsi/libfc/fc_disc.c
>> @@ -47,7 +47,7 @@ static void fc_disc_gpn_ft_req(struct fc_disc *);
>> static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
>> static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
>> static void fc_disc_timeout(struct work_struct *);
>> -static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
>> +static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
>> static void fc_disc_restart(struct fc_disc *);
>>
>> /**
>> @@ -83,7 +83,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp,
>> struct fc_frame *fp,
>> struct fc_disc *disc)
>> {
>> struct fc_lport *lport;
>> - struct fc_rport_priv *rdata;
>> struct fc_els_rscn *rp;
>> struct fc_els_rscn_page *pp;
>> struct fc_seq_els_data rjt_data;
>> @@ -153,6 +152,19 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp,
>> struct fc_frame *fp,
>> }
>> }
>> lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
>> +
>> + /*
>> + * If not doing a complete rediscovery, do GPN_ID on
>> + * the individual ports mentioned in the list.
>> + * If any of these get an error, do a full rediscovery.
>> + * In any case, go through the list and free the entries.
>> + */
>> + list_for_each_entry_safe(dp, next, &disc_ports, peers) {
>> + list_del(&dp->peers);
>> + if (!redisc)
>> + redisc = fc_disc_single(lport, dp);
>> + kfree(dp);
>> + }
>> if (redisc) {
>> FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
>> fc_disc_restart(disc);
>> @@ -160,14 +172,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp,
>> struct fc_frame *fp,
>> FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
>> "redisc %d state %d in_prog %d\n",
>> redisc, lport->state, disc->pending);
>> - list_for_each_entry_safe(dp, next, &disc_ports, peers) {
>> - list_del(&dp->peers);
>> - rdata = lport->tt.rport_lookup(lport, dp->ids.port_id);
>> - if (rdata) {
>> - lport->tt.rport_logoff(rdata);
>> - }
>> - fc_disc_single(disc, dp);
>> - }
>> }
>> fc_frame_free(fp);
>> return;
>> @@ -566,32 +570,119 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp,
>> struct fc_frame *fp,
>> }
>>
>> /**
>> - * fc_disc_single() - Discover the directory information for a single target
>> - * @lport: FC local port
>> - * @dp: The port to rediscover
>> + * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names
>> (GPN_ID)
>> + * @sp: exchange sequence
>> + * @fp: response frame
>> + * @rdata_arg: remote port private data
>> *
>> - * Locking Note: This function expects that the disc_mutex is locked
>> - * before it is called.
>> + * Locking Note: This function is called without disc mutex held.
>> */
>> -static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
>> +static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
>> + void *rdata_arg)
>> {
>> + struct fc_rport_priv *rdata = rdata_arg;
>> + struct fc_rport_priv *new_rdata;
>> struct fc_lport *lport;
>> - struct fc_rport_priv *rdata;
>> + struct fc_disc *disc;
>> + struct fc_ct_hdr *cp;
>> + struct fc_ns_gid_pn *pn;
>> + struct fc_rport_identifiers ids;
>>
>> - lport = disc->lport;
>> + lport = rdata->local_port;
>> + disc = &lport->disc;
>>
>> - if (dp->ids.port_id == fc_host_port_id(lport->host))
>> + mutex_lock(&disc->disc_mutex);
>> + if (PTR_ERR(fp) == -FC_EX_CLOSED)
>> goto out;
>> + if (IS_ERR(fp))
>> + goto redisc;
>> +
>> + cp = fc_frame_payload_get(fp, sizeof(*cp));
>> + if (!cp)
>> + goto redisc;
>> + if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
>> + if (fr_len(fp) < sizeof(struct fc_frame_header) +
>> + sizeof(*cp) + sizeof(*pn))
>> + goto redisc;
>> + pn = (struct fc_ns_gid_pn *)(cp + 1);
>> + ids.port_name = get_unaligned_be64(&pn->fn_wwpn);
>> + if (rdata->ids.port_name == -1)
>> + rdata->ids.port_name = ids.port_name;
>> + else if (rdata->ids.port_name != ids.port_name) {
>> + FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
>> + "Port-id %x wwpn %llx\n",
>> + rdata->ids.port_id,
>> + (unsigned long long)ids.port_name);
>> + lport->tt.rport_logoff(rdata);
>>
>> - rdata = lport->tt.rport_create(lport, &dp->ids);
>> - if (rdata) {
>> + ids.node_name = -1;
>> + ids.roles = FC_PORT_ROLE_UNKNOWN;
>> + new_rdata = lport->tt.rport_create(lport, &ids);
>
> Hey Joe, can you help me understand the intent here?
>
> You want to pass in the port ID of the existing rport/rdata if possible
> in the ids instance, right?
Gee, it looks like ids.port_id never got set up. My mistake.
The port_id will be the same in the old and new instance.
This is so rare a case, maybe impossible; I think it's going to be hard to test.
It's checking to see whether the port_name that it had known previously
is now different in the GPN_ID response. That's really bad if it happens,
and switches are careful to allocate FC_IDs to WWNs so they don't give the
same FC_ID to a new WWPN very soon after it was in use.
We could test it by temporarily forcing it to take that path even if
the port-name matches.
Good catch!
> I've created a patch before these last two series of yours to pass the
> port_id and not the fc_rport_identifiers into rport_create(). I'm going
> through your patches and fixing merge conflicts, but I'm stuck on this
> line- I'm not sure what port_id to pass in.
Use rdata->ids.port_id.
Joe
_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel