From 281ae642a6475ede25ff86ec124214e346c25e22 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 11 Jun 2010 16:43:33 -0700 Subject: [SCSI] libfcoe: FIP link keep-alive should continue while logged off A check in fcoe_ctlr_send_keep_alive() returns if there's no port_id for the local port. This could miss a keep alive if we just did a host reset and have logged off and will log back in. Return only if we are doing the port keep alive, in which case we need to be logged in. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 50aaa4bcfc50..cc5e8864b805 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -343,7 +343,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, fcf = fip->sel_fcf; lp = fip->lp; - if (!fcf || !lp->port_id) + if (!fcf || (ports && !lp->port_id)) return; len = sizeof(*kal) + ports * sizeof(*vn); -- cgit v1.2.3 From 1508f3ecd991ecbf272e08f5ee70d2618f49159e Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 11 Jun 2010 16:43:38 -0700 Subject: [SCSI] libfcoe: Avoid hang when receiving non-critical descriptors Avoid infinite loop while processing FIP ELS or discovery advertisement with non-critical descriptors. Signed-off-by: Bhanu Prakash Gollapudi Acked-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index cc5e8864b805..66120f135d99 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -703,7 +703,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) return -EINVAL; - continue; + break; } desc = (struct fip_desc *)((char *)desc + dlen); rlen -= dlen; @@ -885,7 +885,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) goto drop; - continue; + break; } desc = (struct fip_desc *)((char *)desc + dlen); rlen -= dlen; -- cgit v1.2.3 From 516a648631c912e84e0035590f98eef1d716f4ea Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 11 Jun 2010 16:43:44 -0700 Subject: [SCSI] libfcoe: No solicitation if adv is dropped Host does not send discovery solicitation messages if Disc. Adv from FCF are dropped. It restarts sending solicitation only after receiving a Discovery Adv. from FCF. Fix is to restart solicitation immediately after CVL processing. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 66120f135d99..1d5b949d4fd0 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -1232,8 +1232,11 @@ static void fcoe_ctlr_timer_work(struct work_struct *work) fip->reset_req = 0; spin_unlock_bh(&fip->lock); - if (reset) + if (reset) { fc_lport_reset(fip->lp); + /* restart things with a solicitation */ + fcoe_ctlr_solicit(fip, NULL); + } if (fip->send_ctlr_ka) { fip->send_ctlr_ka = 0; -- cgit v1.2.3 From 8690cb8359d8e9f8d7ca12791ef7ea32b709df8b Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 11 Jun 2010 16:44:10 -0700 Subject: [SCSI] libfcoe: fix lenient aging of FCF advertisements [This patch has several improvements to the code in the fip timers. It hasn't been tested yet. I'm sending it out for review. Vasu, perhaps you can merge this with your patch and test it together.] The current code allows an advertisement to be used even if it has been 3 times the FCF keep-alive advertisement period (FKA) since one was received from that FCF. The spec. calls for 2.5 times FKA. Fix this and make sure we detect missed keep-alives promptly. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 76 +++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 34 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 1d5b949d4fd0..3ab3db39fc52 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -557,38 +557,44 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send); * * Called with lock held and preemption disabled. * - * An FCF is considered old if we have missed three advertisements. - * That is, there have been no valid advertisement from it for three - * times its keep-alive period including fuzz. + * An FCF is considered old if we have missed two advertisements. + * That is, there have been no valid advertisement from it for 2.5 + * times its keep-alive period. * * In addition, determine the time when an FCF selection can occur. * * Also, increment the MissDiscAdvCount when no advertisement is received * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB). + * + * Returns the time in jiffies for the next call. */ -static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) +static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) { struct fcoe_fcf *fcf; struct fcoe_fcf *next; + unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); + unsigned long deadline; unsigned long sel_time = 0; - unsigned long mda_time = 0; struct fcoe_dev_stats *stats; list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { - mda_time = fcf->fka_period + (fcf->fka_period >> 1); - if ((fip->sel_fcf == fcf) && - (time_after(jiffies, fcf->time + mda_time))) { - mod_timer(&fip->timer, jiffies + mda_time); - stats = per_cpu_ptr(fip->lp->dev_stats, - smp_processor_id()); - stats->MissDiscAdvCount++; - printk(KERN_INFO "libfcoe: host%d: Missing Discovery " - "Advertisement for fab %16.16llx count %lld\n", - fip->lp->host->host_no, fcf->fabric_name, - stats->MissDiscAdvCount); + deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2; + if (fip->sel_fcf == fcf) { + if (time_after(jiffies, deadline)) { + stats = per_cpu_ptr(fip->lp->dev_stats, + smp_processor_id()); + stats->MissDiscAdvCount++; + printk(KERN_INFO "libfcoe: host%d: " + "Missing Discovery Advertisement " + "for fab %16.16llx count %lld\n", + fip->lp->host->host_no, fcf->fabric_name, + stats->MissDiscAdvCount); + } else if (time_after(next_timer, deadline)) + next_timer = deadline; } - if (time_after(jiffies, fcf->time + fcf->fka_period * 3 + - msecs_to_jiffies(FIP_FCF_FUZZ * 3))) { + + deadline += fcf->fka_period; + if (time_after(jiffies, deadline)) { if (fip->sel_fcf == fcf) fip->sel_fcf = NULL; list_del(&fcf->list); @@ -598,19 +604,21 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) stats = per_cpu_ptr(fip->lp->dev_stats, smp_processor_id()); stats->VLinkFailureCount++; - } else if (fcoe_ctlr_mtu_valid(fcf) && - (!sel_time || time_before(sel_time, fcf->time))) { - sel_time = fcf->time; + } else { + if (time_after(next_timer, deadline)) + next_timer = deadline; + if (fcoe_ctlr_mtu_valid(fcf) && + (!sel_time || time_before(sel_time, fcf->time))) + sel_time = fcf->time; } } if (sel_time) { sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY); fip->sel_time = sel_time; - if (time_before(sel_time, fip->timer.expires)) - mod_timer(&fip->timer, sel_time); } else { fip->sel_time = 0; } + return next_timer; } /** @@ -1148,7 +1156,7 @@ static void fcoe_ctlr_timeout(unsigned long arg) struct fcoe_ctlr *fip = (struct fcoe_ctlr *)arg; struct fcoe_fcf *sel; struct fcoe_fcf *fcf; - unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); + unsigned long next_timer; spin_lock_bh(&fip->lock); if (fip->state == FIP_ST_DISABLED) { @@ -1157,13 +1165,16 @@ static void fcoe_ctlr_timeout(unsigned long arg) } fcf = fip->sel_fcf; - fcoe_ctlr_age_fcfs(fip); + next_timer = fcoe_ctlr_age_fcfs(fip); sel = fip->sel_fcf; - if (!sel && fip->sel_time && time_after_eq(jiffies, fip->sel_time)) { - fcoe_ctlr_select(fip); - sel = fip->sel_fcf; - fip->sel_time = 0; + if (!sel && fip->sel_time) { + if (time_after_eq(jiffies, fip->sel_time)) { + fcoe_ctlr_select(fip); + sel = fip->sel_fcf; + fip->sel_time = 0; + } else if (time_after(next_timer, fip->sel_time)) + next_timer = fip->sel_time; } if (sel != fcf) { @@ -1201,12 +1212,9 @@ static void fcoe_ctlr_timeout(unsigned long arg) } if (time_after(next_timer, fip->port_ka_time)) next_timer = fip->port_ka_time; - mod_timer(&fip->timer, next_timer); - } else if (fip->sel_time) { - next_timer = fip->sel_time + - msecs_to_jiffies(FCOE_CTLR_START_DELAY); - mod_timer(&fip->timer, next_timer); } + if (!list_empty(&fip->fcfs)) + mod_timer(&fip->timer, next_timer); if (fip->send_ctlr_ka || fip->send_port_ka) schedule_work(&fip->timer_work); spin_unlock_bh(&fip->lock); -- cgit v1.2.3 From d99ee45b7cb89803b79745dc3014ba64bfd02b7d Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 11 Jun 2010 16:44:15 -0700 Subject: [SCSI] libfcoe: Use fka_period as periodic timeouts to age out fcf if keep alives are disabled due to fd_flags set and also stop updating keep alive values in that case. Update select fcf time only if fcf is not already selected or select time is not already determined from parse adv, and then have select time cleared only once after fcf is selected. Changed deadline check to time_after_eq() from time_after() since now next timeout will be on exact 2.5 times FKA followed by first advertisement. Signed-off-by: Vasu Dev Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 3ab3db39fc52..4893098b3d3f 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -594,7 +594,7 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) } deadline += fcf->fka_period; - if (time_after(jiffies, deadline)) { + if (time_after_eq(jiffies, deadline)) { if (fip->sel_fcf == fcf) fip->sel_fcf = NULL; list_del(&fcf->list); @@ -612,12 +612,11 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) sel_time = fcf->time; } } - if (sel_time) { + if (sel_time && !fip->sel_fcf && !fip->sel_time) { sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY); fip->sel_time = sel_time; - } else { - fip->sel_time = 0; } + return next_timer; } @@ -775,7 +774,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) * ignored after a usable solicited advertisement * has been received. */ - if (fcf == fip->sel_fcf) { + if (fcf == fip->sel_fcf && !fcf->fd_flags) { fip->ctlr_ka_time -= fcf->fka_period; fip->ctlr_ka_time += new.fka_period; if (time_before(fip->ctlr_ka_time, fip->timer.expires)) @@ -813,7 +812,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) * If this is the first validated FCF, note the time and * set a timer to trigger selection. */ - if (mtu_valid && !fip->sel_time && fcoe_ctlr_fcf_usable(fcf)) { + if (mtu_valid && !fip->sel_fcf && fcoe_ctlr_fcf_usable(fcf)) { fip->sel_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY); if (!timer_pending(&fip->timer) || @@ -1187,6 +1186,8 @@ static void fcoe_ctlr_timeout(unsigned long arg) fip->port_ka_time = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); fip->ctlr_ka_time = jiffies + sel->fka_period; + if (time_after(next_timer, fip->ctlr_ka_time)) + next_timer = fip->ctlr_ka_time; } else { printk(KERN_NOTICE "libfcoe: host%d: " "FIP Fibre-Channel Forwarder timed out. " -- cgit v1.2.3 From c600fea2d813e8734748202970722c3b6a76b9a1 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 11 Jun 2010 16:44:20 -0700 Subject: [SCSI] libfcoe: update FIP FCF D flag from advertisments Allow the D flag (indicating that keep-alives are not needed) to be updated dynamically from received FIP advertisements. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 4893098b3d3f..27c21ca802b2 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -769,18 +769,21 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) list_add(&fcf->list, &fip->fcfs); } else { /* - * Flags in advertisements are ignored once the FCF is - * selected. Flags in unsolicited advertisements are - * ignored after a usable solicited advertisement - * has been received. + * Update the FCF's keep-alive descriptor flags. + * Other flag changes from new advertisements are + * ignored after a solicited advertisement is + * received and the FCF is selectable (usable). */ + fcf->fd_flags = new.fd_flags; + if (!fcoe_ctlr_fcf_usable(fcf)) + fcf->flags = new.flags; + if (fcf == fip->sel_fcf && !fcf->fd_flags) { fip->ctlr_ka_time -= fcf->fka_period; fip->ctlr_ka_time += new.fka_period; if (time_before(fip->ctlr_ka_time, fip->timer.expires)) mod_timer(&fip->timer, fip->ctlr_ka_time); - } else if (!fcoe_ctlr_fcf_usable(fcf)) - fcf->flags = new.flags; + } fcf->fka_period = new.fka_period; memcpy(fcf->fcf_mac, new.fcf_mac, ETH_ALEN); } -- cgit v1.2.3 From 0a9c5d344dbd983af59865f9880621e5cbbda899 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 11 Jun 2010 16:44:25 -0700 Subject: [SCSI] libfcoe: Handle duplicate critical descriptors As per FC-BB-5 rev 2, section 7.8.6.2, malformed FIP frame shall be discarded. Drop discovery adv, ELS and CLV's with duplicate critical descriptors. [Resending after incorporating Joe's review comments] Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 27c21ca802b2..db7185c0b964 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -640,6 +640,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, unsigned long t; size_t rlen; size_t dlen; + u32 desc_mask; memset(fcf, 0, sizeof(*fcf)); fcf->fka_period = msecs_to_jiffies(FCOE_CTLR_DEF_FKA); @@ -647,6 +648,12 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, fiph = (struct fip_header *)skb->data; fcf->flags = ntohs(fiph->fip_flags); + /* + * mask of required descriptors. validating each one clears its bit. + */ + desc_mask = BIT(FIP_DT_PRI) | BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | + BIT(FIP_DT_FAB) | BIT(FIP_DT_FKA); + rlen = ntohs(fiph->fip_dl_len) * 4; if (rlen + sizeof(*fiph) > skb->len) return -EINVAL; @@ -656,11 +663,19 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, dlen = desc->fip_dlen * FIP_BPW; if (dlen < sizeof(*desc) || dlen > rlen) return -EINVAL; + /* Drop Adv if there are duplicate critical descriptors */ + if ((desc->fip_dtype < 32) && + !(desc_mask & 1U << desc->fip_dtype)) { + LIBFCOE_FIP_DBG(fip, "Duplicate Critical " + "Descriptors in FIP adv\n"); + return -EINVAL; + } switch (desc->fip_dtype) { case FIP_DT_PRI: if (dlen != sizeof(struct fip_pri_desc)) goto len_err; fcf->pri = ((struct fip_pri_desc *)desc)->fd_pri; + desc_mask &= ~BIT(FIP_DT_PRI); break; case FIP_DT_MAC: if (dlen != sizeof(struct fip_mac_desc)) @@ -673,12 +688,14 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, "in FIP adv\n"); return -EINVAL; } + desc_mask &= ~BIT(FIP_DT_MAC); break; case FIP_DT_NAME: if (dlen != sizeof(struct fip_wwn_desc)) goto len_err; wwn = (struct fip_wwn_desc *)desc; fcf->switch_name = get_unaligned_be64(&wwn->fd_wwn); + desc_mask &= ~BIT(FIP_DT_NAME); break; case FIP_DT_FAB: if (dlen != sizeof(struct fip_fab_desc)) @@ -687,6 +704,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, fcf->fabric_name = get_unaligned_be64(&fab->fd_wwn); fcf->vfid = ntohs(fab->fd_vfid); fcf->fc_map = ntoh24(fab->fd_map); + desc_mask &= ~BIT(FIP_DT_FAB); break; case FIP_DT_FKA: if (dlen != sizeof(struct fip_fka_desc)) @@ -697,6 +715,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, t = ntohl(fka->fd_fka_period); if (t >= FCOE_CTLR_MIN_FKA) fcf->fka_period = msecs_to_jiffies(t); + desc_mask &= ~BIT(FIP_DT_FKA); break; case FIP_DT_MAP_OUI: case FIP_DT_FCOE_SIZE: @@ -719,6 +738,11 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, return -EINVAL; if (!fcf->switch_name || !fcf->fabric_name) return -EINVAL; + if (desc_mask) { + LIBFCOE_FIP_DBG(fip, "adv missing descriptors mask %x\n", + desc_mask); + return -EINVAL; + } return 0; len_err: @@ -847,6 +871,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) size_t els_len = 0; size_t rlen; size_t dlen; + u32 dupl_desc = 0; fiph = (struct fip_header *)skb->data; sub = fiph->fip_subcode; @@ -862,6 +887,15 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) dlen = desc->fip_dlen * FIP_BPW; if (dlen < sizeof(*desc) || dlen > rlen) goto drop; + /* Drop ELS if there are duplicate critical descriptors */ + if (desc->fip_dtype < 32) { + if (dupl_desc & 1U << desc->fip_dtype) { + LIBFCOE_FIP_DBG(fip, "Duplicate Critical " + "Descriptors in FIP ELS\n"); + goto drop; + } + dupl_desc |= (1 << desc->fip_dtype); + } switch (desc->fip_dtype) { case FIP_DT_MAC: if (dlen != sizeof(struct fip_mac_desc)) @@ -973,6 +1007,13 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, dlen = desc->fip_dlen * FIP_BPW; if (dlen > rlen) return; + /* Drop CVL if there are duplicate critical descriptors */ + if ((desc->fip_dtype < 32) && + !(desc_mask & 1U << desc->fip_dtype)) { + LIBFCOE_FIP_DBG(fip, "Duplicate Critical " + "Descriptors in FIP CVL\n"); + return; + } switch (desc->fip_dtype) { case FIP_DT_MAC: mp = (struct fip_mac_desc *)desc; -- cgit v1.2.3 From 5550fda73d8bd3bed454e28c46f5a4e5288769bb Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 11 Jun 2010 16:44:31 -0700 Subject: [SCSI] libfcoe: Host doesnt handle CVL to NPIV ports Clear virtual link for NPIV ports is now handled by resetting the matching vnport. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index db7185c0b964..ce651ca2a4bc 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -989,7 +989,9 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, size_t dlen; struct fcoe_fcf *fcf = fip->sel_fcf; struct fc_lport *lport = fip->lp; - u32 desc_mask; + struct fc_lport *vn_port = NULL; + u32 desc_mask; + int is_vn_port = 0; LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); @@ -1038,8 +1040,26 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, if (compare_ether_addr(vp->fd_mac, fip->get_src_addr(lport)) == 0 && get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn && - ntoh24(vp->fd_fc_id) == lport->port_id) + ntoh24(vp->fd_fc_id) == lport->port_id) { desc_mask &= ~BIT(FIP_DT_VN_ID); + break; + } + /* check if clr_vlink is for NPIV port */ + mutex_lock(&lport->lp_mutex); + list_for_each_entry(vn_port, &lport->vports, list) { + if (compare_ether_addr(vp->fd_mac, + fip->get_src_addr(vn_port)) == 0 && + (get_unaligned_be64(&vp->fd_wwpn) + == vn_port->wwpn) && + (ntoh24(vp->fd_fc_id) == + fc_host_port_id(vn_port->host))) { + desc_mask &= ~BIT(FIP_DT_VN_ID); + is_vn_port = 1; + break; + } + } + mutex_unlock(&lport->lp_mutex); + break; default: /* standard says ignore unknown descriptors >= 128 */ @@ -1060,14 +1080,18 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, } else { LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); - spin_lock_bh(&fip->lock); - per_cpu_ptr(lport->dev_stats, - smp_processor_id())->VLinkFailureCount++; - fcoe_ctlr_reset(fip); - spin_unlock_bh(&fip->lock); + if (is_vn_port) + fc_lport_reset(vn_port); + else { + spin_lock_bh(&fip->lock); + per_cpu_ptr(lport->dev_stats, + smp_processor_id())->VLinkFailureCount++; + fcoe_ctlr_reset(fip); + spin_unlock_bh(&fip->lock); - fc_lport_reset(fip->lp); - fcoe_ctlr_solicit(fip, NULL); + fc_lport_reset(fip->lp); + fcoe_ctlr_solicit(fip, NULL); + } } } -- cgit v1.2.3 From be61331d902e63011138723da3f737d34506f797 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 11 Jun 2010 16:44:36 -0700 Subject: [SCSI] libfcoe: Check for order and missing critical descriptors for FIP ELS requests As per FC-BB-5 rev.2, section 7.8.7.1, strict ordering of FIP descriptors is required for ELS requests. Also, look for missing and duplicate critical descriptors. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index ce651ca2a4bc..f009191063f1 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -871,7 +871,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) size_t els_len = 0; size_t rlen; size_t dlen; - u32 dupl_desc = 0; + u32 desc_mask = 0; + u32 desc_cnt = 0; fiph = (struct fip_header *)skb->data; sub = fiph->fip_subcode; @@ -884,20 +885,27 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) desc = (struct fip_desc *)(fiph + 1); while (rlen > 0) { + desc_cnt++; dlen = desc->fip_dlen * FIP_BPW; if (dlen < sizeof(*desc) || dlen > rlen) goto drop; /* Drop ELS if there are duplicate critical descriptors */ if (desc->fip_dtype < 32) { - if (dupl_desc & 1U << desc->fip_dtype) { + if (desc_mask & 1U << desc->fip_dtype) { LIBFCOE_FIP_DBG(fip, "Duplicate Critical " "Descriptors in FIP ELS\n"); goto drop; } - dupl_desc |= (1 << desc->fip_dtype); + desc_mask |= (1 << desc->fip_dtype); } switch (desc->fip_dtype) { case FIP_DT_MAC: + if (desc_cnt == 1) { + LIBFCOE_FIP_DBG(fip, "FIP descriptors " + "received out of order\n"); + goto drop; + } + if (dlen != sizeof(struct fip_mac_desc)) goto len_err; memcpy(granted_mac, @@ -914,6 +922,11 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) case FIP_DT_FDISC: case FIP_DT_LOGO: case FIP_DT_ELP: + if (desc_cnt != 1) { + LIBFCOE_FIP_DBG(fip, "FIP descriptors " + "received out of order\n"); + goto drop; + } if (fh) goto drop; if (dlen < sizeof(*els) + sizeof(*fh) + 1) @@ -929,6 +942,11 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) goto drop; + if (desc_cnt <= 2) { + LIBFCOE_FIP_DBG(fip, "FIP descriptors " + "received out of order\n"); + goto drop; + } break; } desc = (struct fip_desc *)((char *)desc + dlen); @@ -944,6 +962,13 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) fip->flogi_oxid = FC_XID_UNKNOWN; + if ((desc_cnt == 0) || ((els_op != ELS_LS_RJT) && + (!(1U << FIP_DT_MAC & desc_mask)))) { + LIBFCOE_FIP_DBG(fip, "Missing critical descriptors " + "in FIP ELS\n"); + goto drop; + } + /* * Convert skb into an fc_frame containing only the ELS. */ -- cgit v1.2.3 From 9d4cbc05f32fc0a1024de2a9d5635bc9d180e4ae Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Tue, 20 Jul 2010 15:19:26 -0700 Subject: [SCSI] fcoe: cleans up fcoe_disable and fcoe_enable The fc_fabric_logoff and fc_fabric_login are redundant here after recently added fcoe_ctlr_link_down/up to these functions, therefore this patch removes logoff and login to only use link down and up here. This works best for their current usages with fcoe DCB link down or up. This also works well to avoid EIO errors when fcoe DCB link goes down as lport state moves out of ready quickly from fcoe_ctlr_link_down and that allows re-queuing timed out IOs for this case also. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 44a07593de56..d340cf2d857a 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1918,8 +1918,8 @@ static int fcoe_disable(const char *buffer, struct kernel_param *kp) rtnl_unlock(); if (fcoe) { - fc_fabric_logoff(fcoe->ctlr.lp); fcoe_ctlr_link_down(&fcoe->ctlr); + fcoe_clean_pending_queue(fcoe->ctlr.lp); } else rc = -ENODEV; @@ -1972,12 +1972,10 @@ static int fcoe_enable(const char *buffer, struct kernel_param *kp) fcoe = fcoe_hostlist_lookup_port(netdev); rtnl_unlock(); - if (fcoe) { - if (!fcoe_link_ok(fcoe->ctlr.lp)) - fcoe_ctlr_link_up(&fcoe->ctlr); - rc = fc_fabric_login(fcoe->ctlr.lp); - } else + if (!fcoe) rc = -ENODEV; + else if (!fcoe_link_ok(fcoe->ctlr.lp)) + fcoe_ctlr_link_up(&fcoe->ctlr); dev_put(netdev); out_nodev: -- cgit v1.2.3 From 519e5135e2537c9dbc1cbcc0891b0a936ff5dcd2 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Tue, 20 Jul 2010 15:19:32 -0700 Subject: [SCSI] fcoe: adds src and dest mac address checking for fcoe frames This is per FC-BB-5 Annex-D recommendation and per that if address checking fails then drop the frame. FIP code paths are already doing this so only needed for fcoe frames. The src address checking is limited to only fip mode since this might break non-fip mode used in p2p due to used OUI based addressing in some p2p code paths, going forward FIP will be the only mode, therefore limited this to only FIP mode so that it won't break non-fip p2p mode for now. -v2 Removes FCOE packet type checking since fcoe_rcv is registered to receive only FCoE type packets from netdev and it is already checked by netdev. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 20 +++++++++++++++++--- include/scsi/libfcoe.h | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index d340cf2d857a..a120962b25b8 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1210,6 +1210,8 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, struct fcoe_interface *fcoe; struct fc_frame_header *fh; struct fcoe_percpu_s *fps; + struct fcoe_port *port; + struct ethhdr *eh; unsigned int cpu; fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); @@ -1227,9 +1229,21 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, skb_tail_pointer(skb), skb_end_pointer(skb), skb->csum, skb->dev ? skb->dev->name : ""); - /* check for FCOE packet type */ - if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { - FCOE_NETDEV_DBG(netdev, "Wrong FC type frame"); + /* check for mac addresses */ + eh = eth_hdr(skb); + port = lport_priv(lport); + if (compare_ether_addr(eh->h_dest, port->data_src_addr) && + compare_ether_addr(eh->h_dest, fcoe->ctlr.ctl_src_addr) && + compare_ether_addr(eh->h_dest, (u8[6])FC_FCOE_FLOGI_MAC)) { + FCOE_NETDEV_DBG(netdev, "wrong destination mac address:%pM\n", + eh->h_dest); + goto err; + } + + if (is_fip_mode(&fcoe->ctlr) && + compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) { + FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n", + eh->h_source); goto err; } diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index ec13f51531f8..81aee1c4c2f3 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -170,4 +170,14 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *, u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int); int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *); +/** + * is_fip_mode() - returns true if FIP mode selected. + * @fip: FCoE controller. + */ +static inline bool is_fip_mode(struct fcoe_ctlr *fip) +{ + return fip->state == FIP_ST_ENABLED; +} + + #endif /* _LIBFCOE_H */ -- cgit v1.2.3 From fdb068c6cd6e30d43664f856d3530715a5742713 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 20 Jul 2010 15:19:47 -0700 Subject: [SCSI] libfcoe: convert FIP to lock with mutex instead of spin lock It turns out most of the FIP work is now done from worker threads or process context now, so there's no need to use a spin lock. Change to use mutex instead of spin lock and delayed_work instead of a timer. This will make it nicer for the VN_port to VN_port feature that will interact more with the libfc layers requiring that spinlocks not be held. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 116 +++++++++++++++++++++----------------------- include/scsi/libfcoe.h | 9 +--- 2 files changed, 57 insertions(+), 68 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index f009191063f1..e510888e78ca 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -113,7 +113,7 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip) fip->state = FIP_ST_LINK_WAIT; fip->mode = FIP_ST_AUTO; INIT_LIST_HEAD(&fip->fcfs); - spin_lock_init(&fip->lock); + mutex_init(&fip->ctlr_mutex); fip->flogi_oxid = FC_XID_UNKNOWN; setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip); INIT_WORK(&fip->timer_work, fcoe_ctlr_timer_work); @@ -159,10 +159,10 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) cancel_work_sync(&fip->recv_work); skb_queue_purge(&fip->fip_recv_list); - spin_lock_bh(&fip->lock); + mutex_lock(&fip->ctlr_mutex); fip->state = FIP_ST_DISABLED; fcoe_ctlr_reset_fcfs(fip); - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); del_timer_sync(&fip->timer); cancel_work_sync(&fip->timer_work); } @@ -255,19 +255,19 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf) */ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) { - spin_lock_bh(&fip->lock); + mutex_lock(&fip->ctlr_mutex); if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) { - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); fc_linkup(fip->lp); } else if (fip->state == FIP_ST_LINK_WAIT) { fip->state = fip->mode; - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); if (fip->state == FIP_ST_AUTO) LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n"); fc_linkup(fip->lp); fcoe_ctlr_solicit(fip, NULL); } else - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); } EXPORT_SYMBOL(fcoe_ctlr_link_up); @@ -300,11 +300,11 @@ int fcoe_ctlr_link_down(struct fcoe_ctlr *fip) int link_dropped; LIBFCOE_FIP_DBG(fip, "link down.\n"); - spin_lock_bh(&fip->lock); + mutex_lock(&fip->ctlr_mutex); fcoe_ctlr_reset(fip); link_dropped = fip->state != FIP_ST_LINK_WAIT; fip->state = FIP_ST_LINK_WAIT; - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); if (link_dropped) fc_linkdown(fip->lp); @@ -577,12 +577,12 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) unsigned long sel_time = 0; struct fcoe_dev_stats *stats; + stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu()); + list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2; if (fip->sel_fcf == fcf) { if (time_after(jiffies, deadline)) { - stats = per_cpu_ptr(fip->lp->dev_stats, - smp_processor_id()); stats->MissDiscAdvCount++; printk(KERN_INFO "libfcoe: host%d: " "Missing Discovery Advertisement " @@ -601,8 +601,6 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) WARN_ON(!fip->fcf_count); fip->fcf_count--; kfree(fcf); - stats = per_cpu_ptr(fip->lp->dev_stats, - smp_processor_id()); stats->VLinkFailureCount++; } else { if (time_after(next_timer, deadline)) @@ -612,6 +610,7 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) sel_time = fcf->time; } } + put_cpu(); if (sel_time && !fip->sel_fcf && !fip->sel_time) { sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY); fip->sel_time = sel_time; @@ -768,7 +767,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) if (fcoe_ctlr_parse_adv(fip, skb, &new)) return; - spin_lock_bh(&fip->lock); + mutex_lock(&fip->ctlr_mutex); first = list_empty(&fip->fcfs); found = NULL; list_for_each_entry(fcf, &fip->fcfs, list) { @@ -847,7 +846,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) mod_timer(&fip->timer, fip->sel_time); } out: - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); } /** @@ -1108,11 +1107,12 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, if (is_vn_port) fc_lport_reset(vn_port); else { - spin_lock_bh(&fip->lock); + mutex_lock(&fip->ctlr_mutex); per_cpu_ptr(lport->dev_stats, - smp_processor_id())->VLinkFailureCount++; + get_cpu())->VLinkFailureCount++; + put_cpu(); fcoe_ctlr_reset(fip); - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); fc_lport_reset(fip->lp); fcoe_ctlr_solicit(fip, NULL); @@ -1166,7 +1166,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len) goto drop; - spin_lock_bh(&fip->lock); + mutex_lock(&fip->ctlr_mutex); state = fip->state; if (state == FIP_ST_AUTO) { fip->map_dest = 0; @@ -1174,7 +1174,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) state = FIP_ST_ENABLED; LIBFCOE_FIP_DBG(fip, "Using FIP mode\n"); } - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); if (state != FIP_ST_ENABLED) goto drop; @@ -1240,19 +1240,38 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) /** * fcoe_ctlr_timeout() - FIP timeout handler * @arg: The FCoE controller that timed out - * - * Ages FCFs. Triggers FCF selection if possible. Sends keep-alives. */ static void fcoe_ctlr_timeout(unsigned long arg) { struct fcoe_ctlr *fip = (struct fcoe_ctlr *)arg; + + schedule_work(&fip->timer_work); +} + +/** + * fcoe_ctlr_timer_work() - Worker thread function for timer work + * @work: Handle to a FCoE controller + * + * Ages FCFs. Triggers FCF selection if possible. + * Sends keep-alives and resets. + */ +static void fcoe_ctlr_timer_work(struct work_struct *work) +{ + struct fcoe_ctlr *fip; + struct fc_lport *vport; + u8 *mac; + u8 reset = 0; + u8 send_ctlr_ka = 0; + u8 send_port_ka = 0; struct fcoe_fcf *sel; struct fcoe_fcf *fcf; unsigned long next_timer; - spin_lock_bh(&fip->lock); + fip = container_of(work, struct fcoe_ctlr, timer_work); + + mutex_lock(&fip->ctlr_mutex); if (fip->state == FIP_ST_DISABLED) { - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); return; } @@ -1286,7 +1305,7 @@ static void fcoe_ctlr_timeout(unsigned long arg) "FIP Fibre-Channel Forwarder timed out. " "Starting FCF discovery.\n", fip->lp->host->host_no); - fip->reset_req = 1; + reset = 1; schedule_work(&fip->timer_work); } } @@ -1294,7 +1313,7 @@ static void fcoe_ctlr_timeout(unsigned long arg) if (sel && !sel->fd_flags) { if (time_after_eq(jiffies, fip->ctlr_ka_time)) { fip->ctlr_ka_time = jiffies + sel->fka_period; - fip->send_ctlr_ka = 1; + send_ctlr_ka = 1; } if (time_after(next_timer, fip->ctlr_ka_time)) next_timer = fip->ctlr_ka_time; @@ -1302,37 +1321,14 @@ static void fcoe_ctlr_timeout(unsigned long arg) if (time_after_eq(jiffies, fip->port_ka_time)) { fip->port_ka_time = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); - fip->send_port_ka = 1; + send_port_ka = 1; } if (time_after(next_timer, fip->port_ka_time)) next_timer = fip->port_ka_time; } if (!list_empty(&fip->fcfs)) mod_timer(&fip->timer, next_timer); - if (fip->send_ctlr_ka || fip->send_port_ka) - schedule_work(&fip->timer_work); - spin_unlock_bh(&fip->lock); -} - -/** - * fcoe_ctlr_timer_work() - Worker thread function for timer work - * @work: Handle to a FCoE controller - * - * Sends keep-alives and resets which must not - * be called from the timer directly, since they use a mutex. - */ -static void fcoe_ctlr_timer_work(struct work_struct *work) -{ - struct fcoe_ctlr *fip; - struct fc_lport *vport; - u8 *mac; - int reset; - - fip = container_of(work, struct fcoe_ctlr, timer_work); - spin_lock_bh(&fip->lock); - reset = fip->reset_req; - fip->reset_req = 0; - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); if (reset) { fc_lport_reset(fip->lp); @@ -1340,12 +1336,10 @@ static void fcoe_ctlr_timer_work(struct work_struct *work) fcoe_ctlr_solicit(fip, NULL); } - if (fip->send_ctlr_ka) { - fip->send_ctlr_ka = 0; + if (send_ctlr_ka) fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr); - } - if (fip->send_port_ka) { - fip->send_port_ka = 0; + + if (send_port_ka) { mutex_lock(&fip->lp->lp_mutex); mac = fip->get_src_addr(fip->lp); fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac); @@ -1402,9 +1396,9 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP && fip->flogi_oxid == ntohs(fh->fh_ox_id)) { - spin_lock_bh(&fip->lock); + mutex_lock(&fip->ctlr_mutex); if (fip->state != FIP_ST_AUTO && fip->state != FIP_ST_NON_FIP) { - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); return -EINVAL; } fip->state = FIP_ST_NON_FIP; @@ -1424,13 +1418,13 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, fip->map_dest = 0; } fip->flogi_oxid = FC_XID_UNKNOWN; - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id); } else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) { /* * Save source MAC for point-to-point responses. */ - spin_lock_bh(&fip->lock); + mutex_lock(&fip->ctlr_mutex); if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) { memcpy(fip->dest_addr, sa, ETH_ALEN); fip->map_dest = 0; @@ -1439,7 +1433,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, "Setting non-FIP mode\n"); fip->state = FIP_ST_NON_FIP; } - spin_unlock_bh(&fip->lock); + mutex_unlock(&fip->ctlr_mutex); } return 0; } diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 81aee1c4c2f3..7d18b500f2c1 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -75,14 +75,12 @@ enum fip_state { * @flogi_count: number of FLOGI attempts in AUTO mode. * @map_dest: use the FC_MAP mode for destination MAC addresses. * @spma: supports SPMA server-provided MACs mode - * @send_ctlr_ka: need to send controller keep alive - * @send_port_ka: need to send port keep alives * @dest_addr: MAC address of the selected FC forwarder. * @ctl_src_addr: the native MAC address of our local port. * @send: LLD-supplied function to handle sending FIP Ethernet frames * @update_mac: LLD-supplied function to handle changes to MAC addresses. * @get_src_addr: LLD-supplied function to supply a source MAC address. - * @lock: lock protecting this structure. + * @ctlr_mutex: lock protecting this structure. * * This structure is used by all FCoE drivers. It contains information * needed by all FCoE low-level drivers (LLDs) as well as internal state @@ -106,18 +104,15 @@ struct fcoe_ctlr { u16 user_mfs; u16 flogi_oxid; u8 flogi_count; - u8 reset_req; u8 map_dest; u8 spma; - u8 send_ctlr_ka; - u8 send_port_ka; u8 dest_addr[ETH_ALEN]; u8 ctl_src_addr[ETH_ALEN]; void (*send)(struct fcoe_ctlr *, struct sk_buff *); void (*update_mac)(struct fc_lport *, u8 *addr); u8 * (*get_src_addr)(struct fc_lport *); - spinlock_t lock; + struct mutex ctlr_mutex; }; /** -- cgit v1.2.3 From 3d902ac09a2812b359edf633425d1327a18399e9 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 20 Jul 2010 15:19:58 -0700 Subject: [SCSI] libfcoe: fcoe: fnic: change fcoe_ctlr_init interface to specify mode There are three modes that libfcoe currently supports, and a new one is coming. Change the fcoe_ctlr_init() interface to add the mode desired. This should not change any functionality. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 2 +- drivers/scsi/fcoe/libfcoe.c | 4 ++-- drivers/scsi/fnic/fnic_main.c | 4 ++-- include/scsi/libfcoe.h | 11 ++++++++++- 4 files changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index a120962b25b8..9d64e08305c7 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -357,7 +357,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) /* * Initialize FIP. */ - fcoe_ctlr_init(&fcoe->ctlr); + fcoe_ctlr_init(&fcoe->ctlr, FIP_MODE_AUTO); fcoe->ctlr.send = fcoe_fip_send; fcoe->ctlr.update_mac = fcoe_update_src_mac; fcoe->ctlr.get_src_addr = fcoe_get_src_mac; diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index e510888e78ca..76056e4c9297 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -108,10 +108,10 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) * fcoe_ctlr_init() - Initialize the FCoE Controller instance * @fip: The FCoE controller to initialize */ -void fcoe_ctlr_init(struct fcoe_ctlr *fip) +void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode) { fip->state = FIP_ST_LINK_WAIT; - fip->mode = FIP_ST_AUTO; + fip->mode = mode; INIT_LIST_HEAD(&fip->fcfs); mutex_init(&fip->ctlr_mutex); fip->flogi_oxid = FC_XID_UNKNOWN; diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 265e73d9cd6f..d0fe1c3345b8 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -617,7 +617,6 @@ static int __devinit fnic_probe(struct pci_dev *pdev, fnic->ctlr.send = fnic_eth_send; fnic->ctlr.update_mac = fnic_update_mac; fnic->ctlr.get_src_addr = fnic_get_mac; - fcoe_ctlr_init(&fnic->ctlr); if (fnic->config.flags & VFCF_FIP_CAPABLE) { shost_printk(KERN_INFO, fnic->lport->host, "firmware supports FIP\n"); @@ -625,10 +624,11 @@ static int __devinit fnic_probe(struct pci_dev *pdev, vnic_dev_packet_filter(fnic->vdev, 1, 1, 0, 0, 0); vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS); vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr); + fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_AUTO); } else { shost_printk(KERN_INFO, fnic->lport->host, "firmware uses non-FIP mode\n"); - fnic->ctlr.mode = FIP_ST_NON_FIP; + fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_NON_FIP); } fnic->state = FNIC_IN_FC_MODE; diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 7d18b500f2c1..1a84a3182da0 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -54,6 +54,15 @@ enum fip_state { FIP_ST_ENABLED, }; +/* + * Modes: + * The mode is the state that is to be entered after link up. + * It must not change after fcoe_ctlr_init() sets it. + */ +#define FIP_MODE_AUTO FIP_ST_AUTO +#define FIP_MODE_NON_FIP FIP_ST_NON_FIP +#define FIP_MODE_FABRIC FIP_ST_ENABLED + /** * struct fcoe_ctlr - FCoE Controller and FIP state * @state: internal FIP state for network link and FIP or non-FIP mode. @@ -152,7 +161,7 @@ struct fcoe_fcf { }; /* FIP API functions */ -void fcoe_ctlr_init(struct fcoe_ctlr *); +void fcoe_ctlr_init(struct fcoe_ctlr *, enum fip_state); void fcoe_ctlr_destroy(struct fcoe_ctlr *); void fcoe_ctlr_link_up(struct fcoe_ctlr *); int fcoe_ctlr_link_down(struct fcoe_ctlr *); -- cgit v1.2.3 From 9b651da900ccfe5581befb46eb06ef781a1d7e74 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 20 Jul 2010 15:20:24 -0700 Subject: [SCSI] libfcoe: add state change debugging Enhancement: add debug messages at all state transitions. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 48 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 76056e4c9297..11f3db5e506b 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -80,6 +80,40 @@ do { \ printk(KERN_INFO "host%d: fip: " fmt, \ (fip)->lp->host->host_no, ##args);) +static const char *fcoe_ctlr_states[] = { + [FIP_ST_DISABLED] = "DISABLED", + [FIP_ST_LINK_WAIT] = "LINK_WAIT", + [FIP_ST_AUTO] = "AUTO", + [FIP_ST_NON_FIP] = "NON_FIP", + [FIP_ST_ENABLED] = "ENABLED", +}; + +static const char *fcoe_ctlr_state(enum fip_state state) +{ + const char *cp = "unknown"; + + if (state < ARRAY_SIZE(fcoe_ctlr_states)) + cp = fcoe_ctlr_states[state]; + if (!cp) + cp = "unknown"; + return cp; +} + +/** + * fcoe_ctlr_set_state() - Set and do debug printing for the new FIP state. + * @fip: The FCoE controller + * @state: The new state + */ +static void fcoe_ctlr_set_state(struct fcoe_ctlr *fip, enum fip_state state) +{ + if (state == fip->state) + return; + if (fip->lp) + LIBFCOE_FIP_DBG(fip, "state %s -> %s\n", + fcoe_ctlr_state(fip->state), fcoe_ctlr_state(state)); + fip->state = state; +} + /** * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid * @fcf: The FCF to check @@ -110,7 +144,7 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) */ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode) { - fip->state = FIP_ST_LINK_WAIT; + fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT); fip->mode = mode; INIT_LIST_HEAD(&fip->fcfs); mutex_init(&fip->ctlr_mutex); @@ -160,7 +194,7 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) skb_queue_purge(&fip->fip_recv_list); mutex_lock(&fip->ctlr_mutex); - fip->state = FIP_ST_DISABLED; + fcoe_ctlr_set_state(fip, FIP_ST_DISABLED); fcoe_ctlr_reset_fcfs(fip); mutex_unlock(&fip->ctlr_mutex); del_timer_sync(&fip->timer); @@ -260,7 +294,7 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) mutex_unlock(&fip->ctlr_mutex); fc_linkup(fip->lp); } else if (fip->state == FIP_ST_LINK_WAIT) { - fip->state = fip->mode; + fcoe_ctlr_set_state(fip, fip->mode); mutex_unlock(&fip->ctlr_mutex); if (fip->state == FIP_ST_AUTO) LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n"); @@ -303,7 +337,7 @@ int fcoe_ctlr_link_down(struct fcoe_ctlr *fip) mutex_lock(&fip->ctlr_mutex); fcoe_ctlr_reset(fip); link_dropped = fip->state != FIP_ST_LINK_WAIT; - fip->state = FIP_ST_LINK_WAIT; + fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT); mutex_unlock(&fip->ctlr_mutex); if (link_dropped) @@ -1170,7 +1204,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) state = fip->state; if (state == FIP_ST_AUTO) { fip->map_dest = 0; - fip->state = FIP_ST_ENABLED; + fcoe_ctlr_set_state(fip, FIP_ST_ENABLED); state = FIP_ST_ENABLED; LIBFCOE_FIP_DBG(fip, "Using FIP mode\n"); } @@ -1401,7 +1435,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, mutex_unlock(&fip->ctlr_mutex); return -EINVAL; } - fip->state = FIP_ST_NON_FIP; + fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP); LIBFCOE_FIP_DBG(fip, "received FLOGI LS_ACC using non-FIP mode\n"); @@ -1431,7 +1465,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, if (fip->state == FIP_ST_AUTO) LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. " "Setting non-FIP mode\n"); - fip->state = FIP_ST_NON_FIP; + fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP); } mutex_unlock(&fip->ctlr_mutex); } -- cgit v1.2.3 From e10f8c667b874a57512c936089092a3d1ef7ab8a Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 20 Jul 2010 15:20:30 -0700 Subject: [SCSI] libfcoe: fcoe: fnic: add FIP VN2VN point-to-multipoint support The FC-BB-6 committee is proposing a new FIP usage model called VN_port to VN_port mode. It allows VN_ports to discover each other over a loss-free L2 Ethernet without any FCF or Fibre-channel fabric services. This is point-to-multipoint. There is also a variant of this called point-to-point which provides for making sure there is just one pair of ports operating over the Ethernet fabric. We add these new states: VNMP_START, _PROBE1, _PROBE2, _CLAIM, and _UP. These usually go quickly in that sequence. After waiting a random amount of time up to 100 ms in START, we select a pseudo-random proposed locally-unique port ID and send out probes in states PROBE1 and PROBE2, 100 ms apart. If no probe responses are heard, we proceed to CLAIM state 400 ms later and send a claim notification. We wait another 400 ms to receive claim responses, which give us a list of the other nodes on the network, including their FC-4 capabilities. After another 400 ms we go to VNMP_UP state and should start interoperating with any of the nodes for whic we receivec claim responses. More details are in the spec.j Add the new mode as FIP_MODE_VN2VN. The driver must specify explicitly that it wants to operate in this mode. There is no automatic detection between point-to-multipoint and fabric mode, and the local port initialization is affected, so it isn't anticipated that there will ever be any such automatic switchover. It may eventually be possible to have both fabric and VN2VN modes on the same L2 network, which may be done by two separate local VN_ports (lports). When in VN2VN mode, FIP replaces libfc's fabric-oriented discovery module with its own simple code that adds remote ports as they are discovered from incoming claim notifications and responses. These hooks are placed by fcoe_disc_init(). A linear list of discovered vn_ports is maintained under the fcoe_ctlr struct. It is expected to be short for now, and accessed infrequently. It is kept under RCU for lock-ordering reasons. The lport and/or rport mutexes may be held when we need to lookup a fcoe_vnport during an ELS send. Change fcoe_ctlr_encaps() to lookup the destination vn_port in the list of peers for the destination MAC address of the FIP-encapsulated frame. Add a new function fcoe_disc_init() to initialize just the discovery portion of libfcoe for VN2VN mode. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 16 +- drivers/scsi/fcoe/libfcoe.c | 1075 ++++++++++++++++++++++++++++++++++++++--- drivers/scsi/fnic/fnic_main.c | 7 +- include/scsi/libfcoe.h | 42 +- 4 files changed, 1071 insertions(+), 69 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 9d64e08305c7..216aba375fe1 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -315,7 +315,11 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, dev_uc_add(netdev, flogi_maddr); if (fip->spma) dev_uc_add(netdev, fip->ctl_src_addr); - dev_mc_add(netdev, FIP_ALL_ENODE_MACS); + if (fip->mode == FIP_MODE_VN2VN) { + dev_mc_add(netdev, FIP_ALL_VN2VN_MACS); + dev_mc_add(netdev, FIP_ALL_P2P_MACS); + } else + dev_mc_add(netdev, FIP_ALL_ENODE_MACS); /* * setup the receive function from ethernet driver @@ -401,7 +405,11 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) dev_uc_del(netdev, flogi_maddr); if (fip->spma) dev_uc_del(netdev, fip->ctl_src_addr); - dev_mc_del(netdev, FIP_ALL_ENODE_MACS); + if (fip->mode == FIP_MODE_VN2VN) { + dev_mc_del(netdev, FIP_ALL_VN2VN_MACS); + dev_mc_del(netdev, FIP_ALL_P2P_MACS); + } else + dev_mc_del(netdev, FIP_ALL_ENODE_MACS); /* Tell the LLD we are done w/ FCoE */ ops = netdev->netdev_ops; @@ -967,7 +975,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, } /* Initialize the library */ - rc = fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ); + rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " "interface\n"); @@ -2533,6 +2541,8 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did, switch (op) { case ELS_FLOGI: case ELS_FDISC: + if (lport->point_to_multipoint) + break; return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp, fip, timeout); case ELS_LOGO: diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 11f3db5e506b..79df78f2b085 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -54,7 +55,15 @@ static void fcoe_ctlr_timeout(unsigned long); static void fcoe_ctlr_timer_work(struct work_struct *); static void fcoe_ctlr_recv_work(struct work_struct *); +static void fcoe_ctlr_vn_start(struct fcoe_ctlr *); +static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *); +static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *); +static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *, u32, u8 *); + static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS; +static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS; +static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS; +static u8 fcoe_all_p2p[ETH_ALEN] = FIP_ALL_P2P_MACS; unsigned int libfcoe_debug_logging; module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR); @@ -86,6 +95,11 @@ static const char *fcoe_ctlr_states[] = { [FIP_ST_AUTO] = "AUTO", [FIP_ST_NON_FIP] = "NON_FIP", [FIP_ST_ENABLED] = "ENABLED", + [FIP_ST_VNMP_START] = "VNMP_START", + [FIP_ST_VNMP_PROBE1] = "VNMP_PROBE1", + [FIP_ST_VNMP_PROBE2] = "VNMP_PROBE2", + [FIP_ST_VNMP_CLAIM] = "VNMP_CLAIM", + [FIP_ST_VNMP_UP] = "VNMP_UP", }; static const char *fcoe_ctlr_state(enum fip_state state) @@ -295,11 +309,25 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) fc_linkup(fip->lp); } else if (fip->state == FIP_ST_LINK_WAIT) { fcoe_ctlr_set_state(fip, fip->mode); - mutex_unlock(&fip->ctlr_mutex); - if (fip->state == FIP_ST_AUTO) + switch (fip->mode) { + default: + LIBFCOE_FIP_DBG(fip, "invalid mode %d\n", fip->mode); + /* fall-through */ + case FIP_MODE_AUTO: LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n"); - fc_linkup(fip->lp); - fcoe_ctlr_solicit(fip, NULL); + /* fall-through */ + case FIP_MODE_FABRIC: + case FIP_MODE_NON_FIP: + mutex_unlock(&fip->ctlr_mutex); + fc_linkup(fip->lp); + fcoe_ctlr_solicit(fip, NULL); + break; + case FIP_MODE_VN2VN: + fcoe_ctlr_vn_start(fip); + mutex_unlock(&fip->ctlr_mutex); + fc_linkup(fip->lp); + break; + } } else mutex_unlock(&fip->ctlr_mutex); } @@ -423,6 +451,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, * @fip: The FCoE controller for the ELS frame * @dtype: The FIP descriptor type for the frame * @skb: The FCoE ELS frame including FC header but no FCoE headers + * @d_id: The destination port ID. * * Returns non-zero error code on failure. * @@ -433,7 +462,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, * Ethernet header. The tailroom is for the FIP MAC descriptor. */ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, - u8 dtype, struct sk_buff *skb) + u8 dtype, struct sk_buff *skb, u32 d_id) { struct fip_encaps_head { struct ethhdr eth; @@ -445,21 +474,24 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, size_t dlen; u16 fip_flags; - fcf = fip->sel_fcf; - if (!fcf) - return -ENODEV; - - /* set flags according to both FCF and lport's capability on SPMA */ - fip_flags = fcf->flags; - fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA : FIP_FL_FPMA; - if (!fip_flags) - return -ENODEV; - dlen = sizeof(struct fip_encaps) + skb->len; /* len before push */ cap = (struct fip_encaps_head *)skb_push(skb, sizeof(*cap)); - memset(cap, 0, sizeof(*cap)); - memcpy(cap->eth.h_dest, fcf->fcf_mac, ETH_ALEN); + + if (lport->point_to_multipoint) { + if (fcoe_ctlr_vn_lookup(fip, d_id, cap->eth.h_dest)) + return -ENODEV; + } else { + fcf = fip->sel_fcf; + if (!fcf) + return -ENODEV; + fip_flags = fcf->flags; + fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA : + FIP_FL_FPMA; + if (!fip_flags) + return -ENODEV; + memcpy(cap->eth.h_dest, fcf->fcf_mac, ETH_ALEN); + } memcpy(cap->eth.h_source, fip->ctl_src_addr, ETH_ALEN); cap->eth.h_proto = htons(ETH_P_FIP); @@ -503,19 +535,22 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, * * The caller must check that the length is a multiple of 4. * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes). + * The the skb must also be an fc_frame. */ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, struct sk_buff *skb) { + struct fc_frame *fp; struct fc_frame_header *fh; u16 old_xid; u8 op; u8 mac[ETH_ALEN]; + fp = container_of(skb, struct fc_frame, skb); fh = (struct fc_frame_header *)skb->data; op = *(u8 *)(fh + 1); - if (op == ELS_FLOGI) { + if (op == ELS_FLOGI && fip->mode != FIP_MODE_VN2VN) { old_xid = fip->flogi_oxid; fip->flogi_oxid = ntohs(fh->fh_ox_id); if (fip->state == FIP_ST_AUTO) { @@ -533,9 +568,8 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, if (fip->state == FIP_ST_NON_FIP) return 0; - if (!fip->sel_fcf) + if (!fip->sel_fcf && fip->mode != FIP_MODE_VN2VN) goto drop; - switch (op) { case ELS_FLOGI: op = FIP_DT_FLOGI; @@ -546,36 +580,49 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, op = FIP_DT_FDISC; break; case ELS_LOGO: - if (fip->state != FIP_ST_ENABLED) - return 0; - if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) - return 0; + if (fip->mode == FIP_MODE_VN2VN) { + if (fip->state != FIP_ST_VNMP_UP) + return -EINVAL; + if (ntoh24(fh->fh_d_id) == FC_FID_FLOGI) + return -EINVAL; + } else { + if (fip->state != FIP_ST_ENABLED) + return 0; + if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) + return 0; + } op = FIP_DT_LOGO; break; case ELS_LS_ACC: - if (fip->flogi_oxid == FC_XID_UNKNOWN) - return 0; - if (!ntoh24(fh->fh_s_id)) - return 0; - if (fip->state == FIP_ST_AUTO) - return 0; /* - * Here we must've gotten an SID by accepting an FLOGI + * If non-FIP, we may have gotten an SID by accepting an FLOGI * from a point-to-point connection. Switch to using * the source mac based on the SID. The destination * MAC in this case would have been set by receving the * FLOGI. */ - fip->flogi_oxid = FC_XID_UNKNOWN; - fc_fcoe_set_mac(mac, fh->fh_d_id); - fip->update_mac(lport, mac); + if (fip->state == FIP_ST_NON_FIP) { + if (fip->flogi_oxid == FC_XID_UNKNOWN) + return 0; + fip->flogi_oxid = FC_XID_UNKNOWN; + fc_fcoe_set_mac(mac, fh->fh_d_id); + fip->update_mac(lport, mac); + } + /* fall through */ + case ELS_LS_RJT: + op = fr_encaps(fp); + if (op) + break; return 0; default: - if (fip->state != FIP_ST_ENABLED) + if (fip->state != FIP_ST_ENABLED && + fip->state != FIP_ST_VNMP_UP) goto drop; return 0; } - if (fcoe_ctlr_encaps(fip, lport, op, skb)) + LIBFCOE_FIP_DBG(fip, "els_send op %u d_id %x\n", + op, ntoh24(fh->fh_d_id)); + if (fcoe_ctlr_encaps(fip, lport, op, skb, ntoh24(fh->fh_d_id))) goto drop; fip->send(fip, skb); return -EINPROGRESS; @@ -717,8 +764,9 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, ((struct fip_mac_desc *)desc)->fd_mac, ETH_ALEN); if (!is_valid_ether_addr(fcf->fcf_mac)) { - LIBFCOE_FIP_DBG(fip, "Invalid MAC address " - "in FIP adv\n"); + LIBFCOE_FIP_DBG(fip, + "Invalid MAC addr %pM in FIP adv\n", + fcf->fcf_mac); return -EINVAL; } desc_mask &= ~BIT(FIP_DT_MAC); @@ -944,12 +992,6 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) memcpy(granted_mac, ((struct fip_mac_desc *)desc)->fd_mac, ETH_ALEN); - if (!is_valid_ether_addr(granted_mac)) { - LIBFCOE_FIP_DBG(fip, "Invalid MAC address " - "in FIP ELS\n"); - goto drop; - } - memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN); break; case FIP_DT_FLOGI: case FIP_DT_FDISC: @@ -990,10 +1032,20 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) goto drop; els_op = *(u8 *)(fh + 1); - if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP && - fip->flogi_oxid == ntohs(fh->fh_ox_id) && - els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) - fip->flogi_oxid = FC_XID_UNKNOWN; + if ((els_dtype == FIP_DT_FLOGI || els_dtype == FIP_DT_FDISC) && + sub == FIP_SC_REP && els_op == ELS_LS_ACC && + fip->mode != FIP_MODE_VN2VN) { + if (!is_valid_ether_addr(granted_mac)) { + LIBFCOE_FIP_DBG(fip, + "Invalid MAC address %pM in FIP ELS\n", + granted_mac); + goto drop; + } + memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN); + + if (fip->flogi_oxid == ntohs(fh->fh_ox_id)) + fip->flogi_oxid = FC_XID_UNKNOWN; + } if ((desc_cnt == 0) || ((els_op != ELS_LS_RJT) && (!(1U << FIP_DT_MAC & desc_mask)))) { @@ -1012,6 +1064,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) fr_sof(fp) = FC_SOF_I3; fr_eof(fp) = FC_EOF_T; fr_dev(fp) = lport; + fr_encaps(fp) = els_dtype; stats = per_cpu_ptr(lport->dev_stats, get_cpu()); stats->RxFrames++; @@ -1188,8 +1241,13 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) if (skb->len < sizeof(*fiph)) goto drop; eh = eth_hdr(skb); - if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) && - compare_ether_addr(eh->h_dest, FIP_ALL_ENODE_MACS)) + if (fip->mode == FIP_MODE_VN2VN) { + if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) && + compare_ether_addr(eh->h_dest, fcoe_all_vn2vn) && + compare_ether_addr(eh->h_dest, fcoe_all_p2p)) + goto drop; + } else if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) && + compare_ether_addr(eh->h_dest, fcoe_all_enode)) goto drop; fiph = (struct fip_header *)skb->data; op = ntohs(fiph->fip_op); @@ -1209,13 +1267,22 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) LIBFCOE_FIP_DBG(fip, "Using FIP mode\n"); } mutex_unlock(&fip->ctlr_mutex); - if (state != FIP_ST_ENABLED) + + if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN) + return fcoe_ctlr_vn_recv(fip, skb); + + if (state != FIP_ST_ENABLED && state != FIP_ST_VNMP_UP && + state != FIP_ST_VNMP_CLAIM) goto drop; if (op == FIP_OP_LS) { fcoe_ctlr_recv_els(fip, skb); /* consumes skb */ return 0; } + + if (state != FIP_ST_ENABLED) + goto drop; + if (op == FIP_OP_DISC && sub == FIP_SC_ADV) fcoe_ctlr_recv_adv(fip, skb); else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) @@ -1302,7 +1369,8 @@ static void fcoe_ctlr_timer_work(struct work_struct *work) unsigned long next_timer; fip = container_of(work, struct fcoe_ctlr, timer_work); - + if (fip->mode == FIP_MODE_VN2VN) + return fcoe_ctlr_vn_timeout(fip); mutex_lock(&fip->ctlr_mutex); if (fip->state == FIP_ST_DISABLED) { mutex_unlock(&fip->ctlr_mutex); @@ -1340,7 +1408,6 @@ static void fcoe_ctlr_timer_work(struct work_struct *work) "Starting FCF discovery.\n", fip->lp->host->host_no); reset = 1; - schedule_work(&fip->timer_work); } } @@ -1514,27 +1581,917 @@ u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN], } EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac); +/** + * fcoe_ctlr_rport() - return the fcoe_rport for a given fc_rport_priv + * @rdata: libfc remote port + */ +static inline struct fcoe_rport *fcoe_ctlr_rport(struct fc_rport_priv *rdata) +{ + return (struct fcoe_rport *)(rdata + 1); +} + +/** + * fcoe_ctlr_vn_send() - Send a FIP VN2VN Probe Request or Reply. + * @fip: The FCoE controller + * @sub: sub-opcode for probe request, reply, or advertisement. + * @dest: The destination Ethernet MAC address + * @min_len: minimum size of the Ethernet payload to be sent + */ +static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip, + enum fip_vn2vn_subcode sub, + const u8 *dest, size_t min_len) +{ + struct sk_buff *skb; + struct fip_frame { + struct ethhdr eth; + struct fip_header fip; + struct fip_mac_desc mac; + struct fip_wwn_desc wwnn; + struct fip_vn_desc vn; + } __attribute__((packed)) *frame; + struct fip_fc4_feat *ff; + struct fip_size_desc *size; + u32 fcp_feat; + size_t len; + size_t dlen; + + len = sizeof(*frame); + dlen = 0; + if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) { + dlen = sizeof(struct fip_fc4_feat) + + sizeof(struct fip_size_desc); + len += dlen; + } + dlen += sizeof(frame->mac) + sizeof(frame->wwnn) + sizeof(frame->vn); + len = max(len, min_len + sizeof(struct ethhdr)); + + skb = dev_alloc_skb(len); + if (!skb) + return; + + frame = (struct fip_frame *)skb->data; + memset(frame, 0, len); + memcpy(frame->eth.h_dest, dest, ETH_ALEN); + memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN); + frame->eth.h_proto = htons(ETH_P_FIP); + + frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER); + frame->fip.fip_op = htons(FIP_OP_VN2VN); + frame->fip.fip_subcode = sub; + frame->fip.fip_dl_len = htons(dlen / FIP_BPW); + + frame->mac.fd_desc.fip_dtype = FIP_DT_MAC; + frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW; + memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN); + + frame->wwnn.fd_desc.fip_dtype = FIP_DT_NAME; + frame->wwnn.fd_desc.fip_dlen = sizeof(frame->wwnn) / FIP_BPW; + put_unaligned_be64(fip->lp->wwnn, &frame->wwnn.fd_wwn); + + frame->vn.fd_desc.fip_dtype = FIP_DT_VN_ID; + frame->vn.fd_desc.fip_dlen = sizeof(frame->vn) / FIP_BPW; + hton24(frame->vn.fd_mac, FIP_VN_FC_MAP); + hton24(frame->vn.fd_mac + 3, fip->port_id); + hton24(frame->vn.fd_fc_id, fip->port_id); + put_unaligned_be64(fip->lp->wwpn, &frame->vn.fd_wwpn); + + /* + * For claims, add FC-4 features. + * TBD: Add interface to get fc-4 types and features from libfc. + */ + if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) { + ff = (struct fip_fc4_feat *)(frame + 1); + ff->fd_desc.fip_dtype = FIP_DT_FC4F; + ff->fd_desc.fip_dlen = sizeof(*ff) / FIP_BPW; + ff->fd_fts = fip->lp->fcts; + + fcp_feat = 0; + if (fip->lp->service_params & FCP_SPPF_INIT_FCN) + fcp_feat |= FCP_FEAT_INIT; + if (fip->lp->service_params & FCP_SPPF_TARG_FCN) + fcp_feat |= FCP_FEAT_TARG; + fcp_feat <<= (FC_TYPE_FCP * 4) % 32; + ff->fd_ff.fd_feat[FC_TYPE_FCP * 4 / 32] = htonl(fcp_feat); + + size = (struct fip_size_desc *)(ff + 1); + size->fd_desc.fip_dtype = FIP_DT_FCOE_SIZE; + size->fd_desc.fip_dlen = sizeof(*size) / FIP_BPW; + size->fd_size = htons(fcoe_ctlr_fcoe_size(fip)); + } + + skb_put(skb, len); + skb->protocol = htons(ETH_P_FIP); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + + fip->send(fip, skb); +} + +/** + * fcoe_ctlr_vn_rport_callback - Event handler for rport events. + * @lport: The lport which is receiving the event + * @rdata: remote port private data + * @event: The event that occured + * + * Locking Note: The rport lock must not be held when calling this function. + */ +static void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport, + struct fc_rport_priv *rdata, + enum fc_rport_event event) +{ + struct fcoe_ctlr *fip = lport->disc.priv; + struct fcoe_rport *frport = fcoe_ctlr_rport(rdata); + + LIBFCOE_FIP_DBG(fip, "vn_rport_callback %x event %d\n", + rdata->ids.port_id, event); + + mutex_lock(&fip->ctlr_mutex); + switch (event) { + case RPORT_EV_READY: + frport->login_count = 0; + break; + case RPORT_EV_LOGO: + case RPORT_EV_FAILED: + case RPORT_EV_STOP: + frport->login_count++; + if (frport->login_count > FCOE_CTLR_VN2VN_LOGIN_LIMIT) { + LIBFCOE_FIP_DBG(fip, + "rport FLOGI limited port_id %6.6x\n", + rdata->ids.port_id); + lport->tt.rport_logoff(rdata); + } + break; + default: + break; + } + mutex_unlock(&fip->ctlr_mutex); +} + +static struct fc_rport_operations fcoe_ctlr_vn_rport_ops = { + .event_callback = fcoe_ctlr_vn_rport_callback, +}; + +/** + * fcoe_ctlr_disc_stop_locked() - stop discovery in VN2VN mode + * @fip: The FCoE controller + * + * Called with ctlr_mutex held. + */ +static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport) +{ + mutex_lock(&lport->disc.disc_mutex); + lport->disc.disc_callback = NULL; + mutex_unlock(&lport->disc.disc_mutex); +} + +/** + * fcoe_ctlr_disc_stop() - stop discovery in VN2VN mode + * @fip: The FCoE controller + * + * Called through the local port template for discovery. + * Called without the ctlr_mutex held. + */ +static void fcoe_ctlr_disc_stop(struct fc_lport *lport) +{ + struct fcoe_ctlr *fip = lport->disc.priv; + + mutex_lock(&fip->ctlr_mutex); + fcoe_ctlr_disc_stop_locked(lport); + mutex_unlock(&fip->ctlr_mutex); +} + +/** + * fcoe_ctlr_disc_stop_final() - stop discovery for shutdown in VN2VN mode + * @fip: The FCoE controller + * + * Called through the local port template for discovery. + * Called without the ctlr_mutex held. + */ +static void fcoe_ctlr_disc_stop_final(struct fc_lport *lport) +{ + fcoe_ctlr_disc_stop(lport); + lport->tt.rport_flush_queue(); + synchronize_rcu(); +} + +/** + * fcoe_ctlr_vn_restart() - VN2VN probe restart with new port_id + * @fip: The FCoE controller + * + * Called with fcoe_ctlr lock held. + */ +static void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip) +{ + unsigned long wait; + u32 port_id; + + fcoe_ctlr_disc_stop_locked(fip->lp); + + /* + * Get proposed port ID. + * If this is the first try after link up, use any previous port_id. + * If there was none, use the low bits of the port_name. + * On subsequent tries, get the next random one. + * Don't use reserved IDs, use another non-zero value, just as random. + */ + port_id = fip->port_id; + if (fip->probe_tries) + port_id = prandom32(&fip->rnd_state) & 0xffff; + else if (!port_id) + port_id = fip->lp->wwpn & 0xffff; + if (!port_id || port_id == 0xffff) + port_id = 1; + fip->port_id = port_id; + + if (fip->probe_tries < FIP_VN_RLIM_COUNT) { + fip->probe_tries++; + wait = random32() % FIP_VN_PROBE_WAIT; + } else + wait = FIP_VN_RLIM_INT; + mod_timer(&fip->timer, jiffies + msecs_to_jiffies(wait)); + fcoe_ctlr_set_state(fip, FIP_ST_VNMP_START); +} + +/** + * fcoe_ctlr_vn_start() - Start in VN2VN mode + * @fip: The FCoE controller + * + * Called with fcoe_ctlr lock held. + */ +static void fcoe_ctlr_vn_start(struct fcoe_ctlr *fip) +{ + fip->probe_tries = 0; + prandom32_seed(&fip->rnd_state, fip->lp->wwpn); + fcoe_ctlr_vn_restart(fip); +} + +/** + * fcoe_ctlr_vn_parse - parse probe request or response + * @fip: The FCoE controller + * @skb: incoming packet + * @rdata: buffer for resulting parsed VN entry plus fcoe_rport + * + * Returns non-zero error number on error. + * Does not consume the packet. + */ +static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip, + struct sk_buff *skb, + struct fc_rport_priv *rdata) +{ + struct fip_header *fiph; + struct fip_desc *desc = NULL; + struct fip_mac_desc *macd = NULL; + struct fip_wwn_desc *wwn = NULL; + struct fip_vn_desc *vn = NULL; + struct fip_size_desc *size = NULL; + struct fcoe_rport *frport; + size_t rlen; + size_t dlen; + u32 desc_mask = 0; + u32 dtype; + u8 sub; + + memset(rdata, 0, sizeof(*rdata) + sizeof(*frport)); + frport = fcoe_ctlr_rport(rdata); + + fiph = (struct fip_header *)skb->data; + frport->flags = ntohs(fiph->fip_flags); + + sub = fiph->fip_subcode; + switch (sub) { + case FIP_SC_VN_PROBE_REQ: + case FIP_SC_VN_PROBE_REP: + case FIP_SC_VN_BEACON: + desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | + BIT(FIP_DT_VN_ID); + break; + case FIP_SC_VN_CLAIM_NOTIFY: + case FIP_SC_VN_CLAIM_REP: + desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | + BIT(FIP_DT_VN_ID) | BIT(FIP_DT_FC4F) | + BIT(FIP_DT_FCOE_SIZE); + break; + default: + LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub); + return -EINVAL; + } + + rlen = ntohs(fiph->fip_dl_len) * 4; + if (rlen + sizeof(*fiph) > skb->len) + return -EINVAL; + + desc = (struct fip_desc *)(fiph + 1); + while (rlen > 0) { + dlen = desc->fip_dlen * FIP_BPW; + if (dlen < sizeof(*desc) || dlen > rlen) + return -EINVAL; + + dtype = desc->fip_dtype; + if (dtype < 32) { + if (!(desc_mask & BIT(dtype))) { + LIBFCOE_FIP_DBG(fip, + "unexpected or duplicated desc " + "desc type %u in " + "FIP VN2VN subtype %u\n", + dtype, sub); + return -EINVAL; + } + desc_mask &= ~BIT(dtype); + } + + switch (dtype) { + case FIP_DT_MAC: + if (dlen != sizeof(struct fip_mac_desc)) + goto len_err; + macd = (struct fip_mac_desc *)desc; + if (!is_valid_ether_addr(macd->fd_mac)) { + LIBFCOE_FIP_DBG(fip, + "Invalid MAC addr %pM in FIP VN2VN\n", + macd->fd_mac); + return -EINVAL; + } + memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN); + break; + case FIP_DT_NAME: + if (dlen != sizeof(struct fip_wwn_desc)) + goto len_err; + wwn = (struct fip_wwn_desc *)desc; + rdata->ids.node_name = get_unaligned_be64(&wwn->fd_wwn); + break; + case FIP_DT_VN_ID: + if (dlen != sizeof(struct fip_vn_desc)) + goto len_err; + vn = (struct fip_vn_desc *)desc; + memcpy(frport->vn_mac, vn->fd_mac, ETH_ALEN); + rdata->ids.port_id = ntoh24(vn->fd_fc_id); + rdata->ids.port_name = get_unaligned_be64(&vn->fd_wwpn); + break; + case FIP_DT_FC4F: + if (dlen != sizeof(struct fip_fc4_feat)) + goto len_err; + break; + case FIP_DT_FCOE_SIZE: + if (dlen != sizeof(struct fip_size_desc)) + goto len_err; + size = (struct fip_size_desc *)desc; + frport->fcoe_len = ntohs(size->fd_size); + break; + default: + LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x " + "in FIP probe\n", dtype); + /* standard says ignore unknown descriptors >= 128 */ + if (dtype < FIP_DT_VENDOR_BASE) + return -EINVAL; + break; + } + desc = (struct fip_desc *)((char *)desc + dlen); + rlen -= dlen; + } + return 0; + +len_err: + LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n", + dtype, dlen); + return -EINVAL; +} + +/** + * fcoe_ctlr_vn_send_claim() - send multicast FIP VN2VN Claim Notification. + * @fip: The FCoE controller + * + * Called with ctlr_mutex held. + */ +static void fcoe_ctlr_vn_send_claim(struct fcoe_ctlr *fip) +{ + fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_NOTIFY, fcoe_all_vn2vn, 0); + fip->sol_time = jiffies; +} + +/** + * fcoe_ctlr_vn_probe_req() - handle incoming VN2VN probe request. + * @fip: The FCoE controller + * @rdata: parsed remote port with frport from the probe request + * + * Called with ctlr_mutex held. + */ +static void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip, + struct fc_rport_priv *rdata) +{ + struct fcoe_rport *frport = fcoe_ctlr_rport(rdata); + + if (rdata->ids.port_id != fip->port_id) + return; + + switch (fip->state) { + case FIP_ST_VNMP_CLAIM: + case FIP_ST_VNMP_UP: + fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP, + frport->enode_mac, 0); + break; + case FIP_ST_VNMP_PROBE1: + case FIP_ST_VNMP_PROBE2: + /* + * Decide whether to reply to the Probe. + * Our selected address is never a "recorded" one, so + * only reply if our WWPN is greater and the + * Probe's REC bit is not set. + * If we don't reply, we will change our address. + */ + if (fip->lp->wwpn > rdata->ids.port_name && + !(frport->flags & FIP_FL_REC_OR_P2P)) { + fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP, + frport->enode_mac, 0); + break; + } + /* fall through */ + case FIP_ST_VNMP_START: + fcoe_ctlr_vn_restart(fip); + break; + default: + break; + } +} + +/** + * fcoe_ctlr_vn_probe_reply() - handle incoming VN2VN probe reply. + * @fip: The FCoE controller + * @rdata: parsed remote port with frport from the probe request + * + * Called with ctlr_mutex held. + */ +static void fcoe_ctlr_vn_probe_reply(struct fcoe_ctlr *fip, + struct fc_rport_priv *rdata) +{ + if (rdata->ids.port_id != fip->port_id) + return; + switch (fip->state) { + case FIP_ST_VNMP_START: + case FIP_ST_VNMP_PROBE1: + case FIP_ST_VNMP_PROBE2: + case FIP_ST_VNMP_CLAIM: + fcoe_ctlr_vn_restart(fip); + break; + case FIP_ST_VNMP_UP: + fcoe_ctlr_vn_send_claim(fip); + break; + default: + break; + } +} + +/** + * fcoe_ctlr_vn_add() - Add a VN2VN entry to the list, based on a claim reply. + * @fip: The FCoE controller + * @new: newly-parsed remote port with frport as a template for new rdata + * + * Called with ctlr_mutex held. + */ +static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new) +{ + struct fc_lport *lport = fip->lp; + struct fc_rport_priv *rdata; + struct fc_rport_identifiers *ids; + struct fcoe_rport *frport; + u32 port_id; + + port_id = new->ids.port_id; + if (port_id == fip->port_id) + return; + + mutex_lock(&lport->disc.disc_mutex); + rdata = lport->tt.rport_create(lport, port_id); + if (!rdata) { + mutex_unlock(&lport->disc.disc_mutex); + return; + } + + rdata->ops = &fcoe_ctlr_vn_rport_ops; + rdata->disc_id = lport->disc.disc_id; + + ids = &rdata->ids; + if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) || + (ids->node_name != -1 && ids->node_name != new->ids.node_name)) + lport->tt.rport_logoff(rdata); + ids->port_name = new->ids.port_name; + ids->node_name = new->ids.node_name; + mutex_unlock(&lport->disc.disc_mutex); + + frport = fcoe_ctlr_rport(rdata); + LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s\n", + port_id, frport->fcoe_len ? "old" : "new"); + *frport = *fcoe_ctlr_rport(new); + frport->time = 0; +} + +/** + * fcoe_ctlr_vn_lookup() - Find VN remote port's MAC address + * @fip: The FCoE controller + * @port_id: The port_id of the remote VN_node + * @mac: buffer which will hold the VN_NODE destination MAC address, if found. + * + * Returns non-zero error if no remote port found. + */ +static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *fip, u32 port_id, u8 *mac) +{ + struct fc_lport *lport = fip->lp; + struct fc_rport_priv *rdata; + struct fcoe_rport *frport; + int ret = -1; + + rcu_read_lock(); + rdata = lport->tt.rport_lookup(lport, port_id); + if (rdata) { + frport = fcoe_ctlr_rport(rdata); + memcpy(mac, frport->enode_mac, ETH_ALEN); + ret = 0; + } + rcu_read_unlock(); + return ret; +} + +/** + * fcoe_ctlr_vn_claim_notify() - handle received FIP VN2VN Claim Notification + * @fip: The FCoE controller + * @new: newly-parsed remote port with frport as a template for new rdata + * + * Called with ctlr_mutex held. + */ +static void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip, + struct fc_rport_priv *new) +{ + struct fcoe_rport *frport = fcoe_ctlr_rport(new); + + if (frport->flags & FIP_FL_REC_OR_P2P) { + fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0); + return; + } + switch (fip->state) { + case FIP_ST_VNMP_START: + case FIP_ST_VNMP_PROBE1: + case FIP_ST_VNMP_PROBE2: + if (new->ids.port_id == fip->port_id) + fcoe_ctlr_vn_restart(fip); + break; + case FIP_ST_VNMP_CLAIM: + case FIP_ST_VNMP_UP: + if (new->ids.port_id == fip->port_id) { + if (new->ids.port_name > fip->lp->wwpn) { + fcoe_ctlr_vn_restart(fip); + break; + } + fcoe_ctlr_vn_send_claim(fip); + break; + } + fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_REP, frport->enode_mac, + min((u32)frport->fcoe_len, + fcoe_ctlr_fcoe_size(fip))); + fcoe_ctlr_vn_add(fip, new); + break; + default: + break; + } +} + +/** + * fcoe_ctlr_vn_claim_resp() - handle received Claim Response + * @fip: The FCoE controller that received the frame + * @new: newly-parsed remote port with frport from the Claim Response + * + * Called with ctlr_mutex held. + */ +static void fcoe_ctlr_vn_claim_resp(struct fcoe_ctlr *fip, + struct fc_rport_priv *new) +{ + LIBFCOE_FIP_DBG(fip, "claim resp from from rport %x - state %s\n", + new->ids.port_id, fcoe_ctlr_state(fip->state)); + if (fip->state == FIP_ST_VNMP_UP || fip->state == FIP_ST_VNMP_CLAIM) + fcoe_ctlr_vn_add(fip, new); +} + +/** + * fcoe_ctlr_vn_beacon() - handle received beacon. + * @fip: The FCoE controller that received the frame + * @new: newly-parsed remote port with frport from the Beacon + * + * Called with ctlr_mutex held. + */ +static void fcoe_ctlr_vn_beacon(struct fcoe_ctlr *fip, + struct fc_rport_priv *new) +{ + struct fc_lport *lport = fip->lp; + struct fc_rport_priv *rdata; + struct fcoe_rport *frport; + + frport = fcoe_ctlr_rport(new); + if (frport->flags & FIP_FL_REC_OR_P2P) { + fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0); + return; + } + mutex_lock(&lport->disc.disc_mutex); + rdata = lport->tt.rport_lookup(lport, new->ids.port_id); + if (rdata) + kref_get(&rdata->kref); + mutex_unlock(&lport->disc.disc_mutex); + if (rdata) { + if (rdata->ids.node_name == new->ids.node_name && + rdata->ids.port_name == new->ids.port_name) { + frport = fcoe_ctlr_rport(rdata); + if (!frport->time && fip->state == FIP_ST_VNMP_UP) + lport->tt.rport_login(rdata); + frport->time = jiffies; + } + kref_put(&rdata->kref, lport->tt.rport_destroy); + return; + } + if (fip->state != FIP_ST_VNMP_UP) + return; + + /* + * Beacon from a new neighbor. + * Send a claim notify if one hasn't been sent recently. + * Don't add the neighbor yet. + */ + LIBFCOE_FIP_DBG(fip, "beacon from new rport %x. sending claim notify\n", + new->ids.port_id); + if (time_after(jiffies, + fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT))) + fcoe_ctlr_vn_send_claim(fip); +} + +/** + * fcoe_ctlr_vn_age() - Check for VN_ports without recent beacons + * @fip: The FCoE controller + * + * Called with ctlr_mutex held. + * Called only in state FIP_ST_VNMP_UP. + * Returns the soonest time for next age-out or a time far in the future. + */ +static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip) +{ + struct fc_lport *lport = fip->lp; + struct fc_rport_priv *rdata; + struct fcoe_rport *frport; + unsigned long next_time; + unsigned long deadline; + + next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10); + mutex_lock(&lport->disc.disc_mutex); + list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) { + frport = fcoe_ctlr_rport(rdata); + if (!frport->time) + continue; + deadline = frport->time + + msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10); + if (time_after_eq(jiffies, deadline)) { + frport->time = 0; + LIBFCOE_FIP_DBG(fip, + "port %16.16llx fc_id %6.6x beacon expired\n", + rdata->ids.port_name, rdata->ids.port_id); + lport->tt.rport_logoff(rdata); + } else if (time_before(deadline, next_time)) + next_time = deadline; + } + mutex_unlock(&lport->disc.disc_mutex); + return next_time; +} + +/** + * fcoe_ctlr_vn_recv() - Receive a FIP frame + * @fip: The FCoE controller that received the frame + * @skb: The received FIP frame + * + * Returns non-zero if the frame is dropped. + * Always consumes the frame. + */ +static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb) +{ + struct fip_header *fiph; + enum fip_vn2vn_subcode sub; + union { + struct fc_rport_priv rdata; + struct fcoe_rport frport; + } buf; + int rc; + + fiph = (struct fip_header *)skb->data; + sub = fiph->fip_subcode; + + rc = fcoe_ctlr_vn_parse(fip, skb, &buf.rdata); + if (rc) { + LIBFCOE_FIP_DBG(fip, "vn_recv vn_parse error %d\n", rc); + goto drop; + } + + mutex_lock(&fip->ctlr_mutex); + switch (sub) { + case FIP_SC_VN_PROBE_REQ: + fcoe_ctlr_vn_probe_req(fip, &buf.rdata); + break; + case FIP_SC_VN_PROBE_REP: + fcoe_ctlr_vn_probe_reply(fip, &buf.rdata); + break; + case FIP_SC_VN_CLAIM_NOTIFY: + fcoe_ctlr_vn_claim_notify(fip, &buf.rdata); + break; + case FIP_SC_VN_CLAIM_REP: + fcoe_ctlr_vn_claim_resp(fip, &buf.rdata); + break; + case FIP_SC_VN_BEACON: + fcoe_ctlr_vn_beacon(fip, &buf.rdata); + break; + default: + LIBFCOE_FIP_DBG(fip, "vn_recv unknown subcode %d\n", sub); + rc = -1; + break; + } + mutex_unlock(&fip->ctlr_mutex); +drop: + kfree_skb(skb); + return rc; +} + +/** + * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode. + * @fip: The FCoE controller + * + * This should never be called since we don't see RSCNs or other + * fabric-generated ELSes. + */ +static void fcoe_ctlr_disc_recv(struct fc_seq *seq, struct fc_frame *fp, + struct fc_lport *lport) +{ + struct fc_seq_els_data rjt_data; + + rjt_data.fp = NULL; + rjt_data.reason = ELS_RJT_UNSUP; + rjt_data.explan = ELS_EXPL_NONE; + lport->tt.seq_els_rsp_send(seq, ELS_LS_RJT, &rjt_data); + fc_frame_free(fp); +} + +/** + * fcoe_ctlr_disc_recv - start discovery for VN2VN mode. + * @fip: The FCoE controller + * + * This sets a flag indicating that remote ports should be created + * and started for the peers we discover. We use the disc_callback + * pointer as that flag. Peers already discovered are created here. + * + * The lport lock is held during this call. The callback must be done + * later, without holding either the lport or discovery locks. + * The fcoe_ctlr lock may also be held during this call. + */ +static void fcoe_ctlr_disc_start(void (*callback)(struct fc_lport *, + enum fc_disc_event), + struct fc_lport *lport) +{ + struct fc_disc *disc = &lport->disc; + struct fcoe_ctlr *fip = disc->priv; + + mutex_lock(&disc->disc_mutex); + disc->disc_callback = callback; + disc->disc_id = (disc->disc_id + 2) | 1; + disc->pending = 1; + schedule_work(&fip->timer_work); + mutex_unlock(&disc->disc_mutex); +} + +/** + * fcoe_ctlr_vn_disc() - report FIP VN_port discovery results after claim state. + * @fip: The FCoE controller + * + * Starts the FLOGI and PLOGI login process to each discovered rport for which + * we've received at least one beacon. + * Performs the discovery complete callback. + */ +static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip) +{ + struct fc_lport *lport = fip->lp; + struct fc_disc *disc = &lport->disc; + struct fc_rport_priv *rdata; + struct fcoe_rport *frport; + void (*callback)(struct fc_lport *, enum fc_disc_event); + + mutex_lock(&disc->disc_mutex); + callback = disc->pending ? disc->disc_callback : NULL; + disc->pending = 0; + list_for_each_entry_rcu(rdata, &disc->rports, peers) { + frport = fcoe_ctlr_rport(rdata); + if (frport->time) + lport->tt.rport_login(rdata); + } + mutex_unlock(&disc->disc_mutex); + if (callback) + callback(lport, DISC_EV_SUCCESS); +} + +/** + * fcoe_ctlr_vn_timeout - timer work function for VN2VN mode. + * @fip: The FCoE controller + */ +static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip) +{ + unsigned long next_time; + u8 mac[ETH_ALEN]; + u32 new_port_id = 0; + + mutex_lock(&fip->ctlr_mutex); + switch (fip->state) { + case FIP_ST_VNMP_START: + fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE1); + fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0); + next_time = jiffies + msecs_to_jiffies(FIP_VN_PROBE_WAIT); + break; + case FIP_ST_VNMP_PROBE1: + fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE2); + fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0); + next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT); + break; + case FIP_ST_VNMP_PROBE2: + fcoe_ctlr_set_state(fip, FIP_ST_VNMP_CLAIM); + new_port_id = fip->port_id; + hton24(mac, FIP_VN_FC_MAP); + hton24(mac + 3, new_port_id); + fip->update_mac(fip->lp, mac); + fcoe_ctlr_vn_send_claim(fip); + next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT); + break; + case FIP_ST_VNMP_CLAIM: + /* + * This may be invoked either by starting discovery so don't + * go to the next state unless it's been long enough. + */ + next_time = fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT); + if (time_after_eq(jiffies, next_time)) { + fcoe_ctlr_set_state(fip, FIP_ST_VNMP_UP); + fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON, + fcoe_all_vn2vn, 0); + next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT); + fip->port_ka_time = next_time; + } + fcoe_ctlr_vn_disc(fip); + break; + case FIP_ST_VNMP_UP: + next_time = fcoe_ctlr_vn_age(fip); + if (time_after_eq(jiffies, fip->port_ka_time)) { + fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON, + fcoe_all_vn2vn, 0); + fip->port_ka_time = jiffies + + msecs_to_jiffies(FIP_VN_BEACON_INT + + (random32() % FIP_VN_BEACON_FUZZ)); + } + if (time_before(fip->port_ka_time, next_time)) + next_time = fip->port_ka_time; + break; + case FIP_ST_LINK_WAIT: + goto unlock; + default: + WARN(1, "unexpected state %d", fip->state); + goto unlock; + } + mod_timer(&fip->timer, next_time); +unlock: + mutex_unlock(&fip->ctlr_mutex); + + /* If port ID is new, notify local port after dropping ctlr_mutex */ + if (new_port_id) + fc_lport_set_local_id(fip->lp, new_port_id); +} + /** * fcoe_libfc_config() - Sets up libfc related properties for local port * @lp: The local port to configure libfc for + * @fip: The FCoE controller in use by the local port * @tt: The libfc function template + * @init_fcp: If non-zero, the FCP portion of libfc should be initialized * * Returns : 0 for success */ -int fcoe_libfc_config(struct fc_lport *lport, - struct libfc_function_template *tt) +int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip, + const struct libfc_function_template *tt, int init_fcp) { /* Set the function pointers set by the LLDD */ memcpy(&lport->tt, tt, sizeof(*tt)); - if (fc_fcp_init(lport)) + if (init_fcp && fc_fcp_init(lport)) return -ENOMEM; fc_exch_init(lport); fc_elsct_init(lport); fc_lport_init(lport); + if (fip->mode == FIP_MODE_VN2VN) + lport->rport_priv_size = sizeof(struct fcoe_rport); fc_rport_init(lport); - fc_disc_init(lport); - + if (fip->mode == FIP_MODE_VN2VN) { + lport->point_to_multipoint = 1; + lport->tt.disc_recv_req = fcoe_ctlr_disc_recv; + lport->tt.disc_start = fcoe_ctlr_disc_start; + lport->tt.disc_stop = fcoe_ctlr_disc_stop; + lport->tt.disc_stop_final = fcoe_ctlr_disc_stop_final; + mutex_init(&lport->disc.disc_mutex); + INIT_LIST_HEAD(&lport->disc.rports); + lport->disc.priv = fip; + } else { + fc_disc_init(lport); + } return 0; } EXPORT_SYMBOL_GPL(fcoe_libfc_config); - diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index d0fe1c3345b8..9eb7a9ebccae 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -673,7 +673,6 @@ static int __devinit fnic_probe(struct pci_dev *pdev, /* Start local port initiatialization */ lp->link_up = 0; - lp->tt = fnic_transport_template; lp->max_retry_count = fnic->config.flogi_retries; lp->max_rport_retry_count = fnic->config.plogi_retries; @@ -689,11 +688,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev, fc_set_wwnn(lp, fnic->config.node_wwn); fc_set_wwpn(lp, fnic->config.port_wwn); - fc_lport_init(lp); - fc_exch_init(lp); - fc_elsct_init(lp); - fc_rport_init(lp); - fc_disc_init(lp); + fcoe_libfc_config(lp, &fnic->ctlr, &fnic_transport_template, 0); if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCPIO_HOST_EXCH_RANGE_START, FCPIO_HOST_EXCH_RANGE_END, NULL)) { diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 1a84a3182da0..06f1b5a8ed19 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,7 @@ #define FCOE_CTLR_START_DELAY 2000 /* mS after first adv. to choose FCF */ #define FCOE_CTRL_SOL_TOV 2000 /* min. solicitation interval (mS) */ #define FCOE_CTLR_FCF_LIMIT 20 /* max. number of FCF entries */ +#define FCOE_CTLR_VN2VN_LOGIN_LIMIT 3 /* max. VN2VN rport login retries */ /** * enum fip_state - internal state of FCoE controller. @@ -45,6 +47,11 @@ * @FIP_ST_AUTO: determining whether to use FIP or non-FIP mode. * @FIP_ST_NON_FIP: non-FIP mode selected. * @FIP_ST_ENABLED: FIP mode selected. + * @FIP_ST_VNMP_START: VN2VN multipath mode start, wait + * @FIP_ST_VNMP_PROBE1: VN2VN sent first probe, listening + * @FIP_ST_VNMP_PROBE2: VN2VN sent second probe, listening + * @FIP_ST_VNMP_CLAIM: VN2VN sent claim, waiting for responses + * @FIP_ST_VNMP_UP: VN2VN multipath mode operation */ enum fip_state { FIP_ST_DISABLED, @@ -52,6 +59,11 @@ enum fip_state { FIP_ST_AUTO, FIP_ST_NON_FIP, FIP_ST_ENABLED, + FIP_ST_VNMP_START, + FIP_ST_VNMP_PROBE1, + FIP_ST_VNMP_PROBE2, + FIP_ST_VNMP_CLAIM, + FIP_ST_VNMP_UP, }; /* @@ -62,6 +74,7 @@ enum fip_state { #define FIP_MODE_AUTO FIP_ST_AUTO #define FIP_MODE_NON_FIP FIP_ST_NON_FIP #define FIP_MODE_FABRIC FIP_ST_ENABLED +#define FIP_MODE_VN2VN FIP_ST_VNMP_START /** * struct fcoe_ctlr - FCoE Controller and FIP state @@ -79,11 +92,14 @@ enum fip_state { * @timer_work: &work_struct for doing keep-alives and resets. * @recv_work: &work_struct for receiving FIP frames. * @fip_recv_list: list of received FIP frames. + * @rnd_state: state for pseudo-random number generator. + * @port_id: proposed or selected local-port ID. * @user_mfs: configured maximum FC frame size, including FC header. * @flogi_oxid: exchange ID of most recent fabric login. * @flogi_count: number of FLOGI attempts in AUTO mode. * @map_dest: use the FC_MAP mode for destination MAC addresses. * @spma: supports SPMA server-provided MACs mode + * @probe_tries: number of FC_IDs probed * @dest_addr: MAC address of the selected FC forwarder. * @ctl_src_addr: the native MAC address of our local port. * @send: LLD-supplied function to handle sending FIP Ethernet frames @@ -110,11 +126,16 @@ struct fcoe_ctlr { struct work_struct timer_work; struct work_struct recv_work; struct sk_buff_head fip_recv_list; + + struct rnd_state rnd_state; + u32 port_id; + u16 user_mfs; u16 flogi_oxid; u8 flogi_count; u8 map_dest; u8 spma; + u8 probe_tries; u8 dest_addr[ETH_ALEN]; u8 ctl_src_addr[ETH_ALEN]; @@ -160,6 +181,24 @@ struct fcoe_fcf { u8 fd_flags:1; }; +/** + * struct fcoe_rport - VN2VN remote port + * @time: time of create or last beacon packet received from node + * @fcoe_len: max FCoE frame size, not including VLAN or Ethernet headers + * @flags: flags from probe or claim + * @login_count: number of unsuccessful rport logins to this port + * @enode_mac: E_Node control MAC address + * @vn_mac: VN_Node assigned MAC address for data + */ +struct fcoe_rport { + unsigned long time; + u16 fcoe_len; + u16 flags; + u8 login_count; + u8 enode_mac[ETH_ALEN]; + u8 vn_mac[ETH_ALEN]; +}; + /* FIP API functions */ void fcoe_ctlr_init(struct fcoe_ctlr *, enum fip_state); void fcoe_ctlr_destroy(struct fcoe_ctlr *); @@ -172,7 +211,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *, /* libfcoe funcs */ u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int); -int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *); +int fcoe_libfc_config(struct fc_lport *, struct fcoe_ctlr *, + const struct libfc_function_template *, int init_fcp); /** * is_fip_mode() - returns true if FIP mode selected. -- cgit v1.2.3 From 5554345bc5275afed760631277fdab0a5a19472e Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 20 Jul 2010 15:20:35 -0700 Subject: [SCSI] libfcoe: Fix FIP ELS encapsulation details for FLOGI responses When sending a FLOGI LS_ACC, which we only do in point-to-multipoint mode, the MAC descriptor should have the granted MAC set to 0x0efd00 || D_ID. When sending an LS_RJT, there should be no MAC descriptor. When sending either an LS_ACC or LS_RJT, the subcode should indicate an reply, not a request. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 79df78f2b085..4865e8181172 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -469,11 +469,15 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, struct fip_header fip; struct fip_encaps encaps; } __attribute__((packed)) *cap; + struct fc_frame_header *fh; struct fip_mac_desc *mac; struct fcoe_fcf *fcf; size_t dlen; u16 fip_flags; + u8 op; + fh = (struct fc_frame_header *)skb->data; + op = *(u8 *)(fh + 1); dlen = sizeof(struct fip_encaps) + skb->len; /* len before push */ cap = (struct fip_encaps_head *)skb_push(skb, sizeof(*cap)); memset(cap, 0, sizeof(*cap)); @@ -481,6 +485,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, if (lport->point_to_multipoint) { if (fcoe_ctlr_vn_lookup(fip, d_id, cap->eth.h_dest)) return -ENODEV; + fip_flags = 0; } else { fcf = fip->sel_fcf; if (!fcf) @@ -497,26 +502,35 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, cap->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER); cap->fip.fip_op = htons(FIP_OP_LS); - cap->fip.fip_subcode = FIP_SC_REQ; - cap->fip.fip_dl_len = htons((dlen + sizeof(*mac)) / FIP_BPW); + if (op == ELS_LS_ACC || op == ELS_LS_RJT) + cap->fip.fip_subcode = FIP_SC_REP; + else + cap->fip.fip_subcode = FIP_SC_REQ; cap->fip.fip_flags = htons(fip_flags); cap->encaps.fd_desc.fip_dtype = dtype; cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW; - mac = (struct fip_mac_desc *)skb_put(skb, sizeof(*mac)); - memset(mac, 0, sizeof(*mac)); - mac->fd_desc.fip_dtype = FIP_DT_MAC; - mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW; - if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) { - memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN); - } else if (fip_flags & FIP_FL_SPMA) { - LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n"); - memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN); - } else { - LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n"); - /* FPMA only FLOGI must leave the MAC desc set to all 0s */ + if (op != ELS_LS_RJT) { + dlen += sizeof(*mac); + mac = (struct fip_mac_desc *)skb_put(skb, sizeof(*mac)); + memset(mac, 0, sizeof(*mac)); + mac->fd_desc.fip_dtype = FIP_DT_MAC; + mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW; + if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) { + memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN); + } else if (fip->mode == FIP_MODE_VN2VN) { + hton24(mac->fd_mac, FIP_VN_FC_MAP); + hton24(mac->fd_mac + 3, fip->port_id); + } else if (fip_flags & FIP_FL_SPMA) { + LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n"); + memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN); + } else { + LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n"); + /* FPMA only FLOGI. Must leave the MAC desc zeroed. */ + } } + cap->fip.fip_dl_len = htons(dlen / FIP_BPW); skb->protocol = htons(ETH_P_FIP); skb_reset_mac_header(skb); -- cgit v1.2.3 From cd229e42eb8cdfdcbe15dfeec39c3641f62de43a Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 20 Jul 2010 15:20:40 -0700 Subject: [SCSI] fcoe libfcoe: use correct FC-MAP for VN2VN mode In VN2VN mode, map_dest means to use the default VN2VN OUI. Change code that uses the default FCoE OUI to use the one set in the fcoe_ctlr struct. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 6 ++---- drivers/scsi/fcoe/libfcoe.c | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 216aba375fe1..4d3e70ab65b5 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1534,11 +1534,9 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) /* fill up mac and fcoe headers */ eh = eth_hdr(skb); eh->h_proto = htons(ETH_P_FCOE); + memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN); if (fcoe->ctlr.map_dest) - fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); - else - /* insert GW address */ - memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN); + memcpy(eh->h_dest + 3, fh->fh_d_id, 3); if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN)) memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN); diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 4865e8181172..4de8ced1fee7 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -152,6 +152,20 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) return (fcf->flags & flags) == flags; } +/** + * fcoe_ctlr_map_dest() - Set flag and OUI for mapping destination addresses + * @fip: The FCoE controller + */ +static void fcoe_ctlr_map_dest(struct fcoe_ctlr *fip) +{ + if (fip->mode == FIP_MODE_VN2VN) + hton24(fip->dest_addr, FIP_VN_FC_MAP); + else + hton24(fip->dest_addr, FIP_DEF_FC_MAP); + hton24(fip->dest_addr + 3, 0); + fip->map_dest = 1; +} + /** * fcoe_ctlr_init() - Initialize the FCoE Controller instance * @fip: The FCoE controller to initialize @@ -345,7 +359,7 @@ static void fcoe_ctlr_reset(struct fcoe_ctlr *fip) fip->port_ka_time = 0; fip->sol_time = 0; fip->flogi_oxid = FC_XID_UNKNOWN; - fip->map_dest = 0; + fcoe_ctlr_map_dest(fip); } /** @@ -573,11 +587,11 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, fip->flogi_count++; if (fip->flogi_count < 3) goto drop; - fip->map_dest = 1; + fcoe_ctlr_map_dest(fip); return 0; } if (fip->state == FIP_ST_NON_FIP) - fip->map_dest = 1; + fcoe_ctlr_map_dest(fip); } if (fip->state == FIP_ST_NON_FIP) @@ -1411,6 +1425,7 @@ static void fcoe_ctlr_timer_work(struct work_struct *work) "Fibre-Channel Forwarder MAC %pM\n", fip->lp->host->host_no, sel->fcf_mac); memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN); + fip->map_dest = 0; fip->port_ka_time = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); fip->ctlr_ka_time = jiffies + sel->fka_period; @@ -1527,7 +1542,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, * Otherwise we use the FCoE gateway addr */ if (!compare_ether_addr(sa, (u8[6])FC_FCOE_FLOGI_MAC)) { - fip->map_dest = 1; + fcoe_ctlr_map_dest(fip); } else { memcpy(fip->dest_addr, sa, ETH_ALEN); fip->map_dest = 0; @@ -2426,6 +2441,7 @@ static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip) new_port_id = fip->port_id; hton24(mac, FIP_VN_FC_MAP); hton24(mac + 3, new_port_id); + fcoe_ctlr_map_dest(fip); fip->update_mac(fip->lp, mac); fcoe_ctlr_vn_send_claim(fip); next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT); -- cgit v1.2.3 From 1dd454d9e5205f9a61d51fb97159afeffa0a506c Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 20 Jul 2010 15:20:46 -0700 Subject: [SCSI] fcoe: config via separate create_vn2vn module parameter Add module parameter create_vn2vn that behaves like the create parameter except that the new instance is created in FIP vn2vn mode. This can be replaced once we change create to allow modifying per-instance attributes before starting the instance. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 4d3e70ab65b5..ab6ea60f2aee 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -117,9 +117,14 @@ static void fcoe_recv_frame(struct sk_buff *skb); static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *); -module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); +module_param_call(create, fcoe_create, NULL, (void *)FIP_MODE_AUTO, S_IWUSR); __MODULE_PARM_TYPE(create, "string"); MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface"); +module_param_call(create_vn2vn, fcoe_create, NULL, + (void *)FIP_MODE_VN2VN, S_IWUSR); +__MODULE_PARM_TYPE(create_vn2vn, "string"); +MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance " + "on an Ethernet interface"); module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); __MODULE_PARM_TYPE(destroy, "string"); MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface"); @@ -341,10 +346,12 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, /** * fcoe_interface_create() - Create a FCoE interface on a net device * @netdev: The net device to create the FCoE interface on + * @fip_mode: The mode to use for FIP * * Returns: pointer to a struct fcoe_interface or NULL on error */ -static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) +static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev, + enum fip_state fip_mode) { struct fcoe_interface *fcoe; int err; @@ -361,7 +368,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) /* * Initialize FIP. */ - fcoe_ctlr_init(&fcoe->ctlr, FIP_MODE_AUTO); + fcoe_ctlr_init(&fcoe->ctlr, fip_mode); fcoe->ctlr.send = fcoe_fip_send; fcoe->ctlr.update_mac = fcoe_update_src_mac; fcoe->ctlr.get_src_addr = fcoe_get_src_mac; @@ -2088,6 +2095,7 @@ static void fcoe_destroy_work(struct work_struct *work) */ static int fcoe_create(const char *buffer, struct kernel_param *kp) { + enum fip_state fip_mode = (enum fip_state)(long)kp->arg; int rc; struct fcoe_interface *fcoe; struct fc_lport *lport; @@ -2129,7 +2137,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) goto out_putdev; } - fcoe = fcoe_interface_create(netdev); + fcoe = fcoe_interface_create(netdev, fip_mode); if (!fcoe) { rc = -ENOMEM; goto out_putdev; -- cgit v1.2.3 From 922611569572d3c1aa0ed6491d21583fb3fcca22 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 20 Jul 2010 15:21:12 -0700 Subject: [SCSI] libfc: don't require a local exchange for incoming requests Incoming requests shouldn't require a local exchange if we're just going to reply with one or two frames and don't expect anything further. Don't allocate exchanges for such requests until requested by the upper-layer protocol. The sequence is always NULL for new requests, so remove that as an argument to request handlers. Also change the first argument to lport->tt.seq_els_rsp_send from the sequence pointer to the received frame pointer, to supply the exchange IDs and destination ID info. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 9 +- drivers/scsi/libfc/fc_disc.c | 19 ++--- drivers/scsi/libfc/fc_exch.c | 188 ++++++++++++++++++++++-------------------- drivers/scsi/libfc/fc_lport.c | 58 +++++-------- drivers/scsi/libfc/fc_rport.c | 112 +++++++++---------------- include/scsi/libfc.h | 16 ++-- 6 files changed, 174 insertions(+), 228 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 4de8ced1fee7..2c265fe9ab32 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -2341,20 +2341,19 @@ drop: /** * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode. - * @fip: The FCoE controller + * @lport: The local port + * @fp: The received frame * * This should never be called since we don't see RSCNs or other * fabric-generated ELSes. */ -static void fcoe_ctlr_disc_recv(struct fc_seq *seq, struct fc_frame *fp, - struct fc_lport *lport) +static void fcoe_ctlr_disc_recv(struct fc_lport *lport, struct fc_frame *fp) { struct fc_seq_els_data rjt_data; - rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_UNSUP; rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(seq, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data); fc_frame_free(fp); } diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 04474556f2dc..32f67c4b03fc 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -75,15 +75,13 @@ void fc_disc_stop_rports(struct fc_disc *disc) /** * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN) - * @sp: The sequence of the RSCN exchange + * @disc: The discovery object to which the RSCN applies * @fp: The RSCN frame - * @lport: The local port that the request will be sent on * * Locking Note: This function expects that the disc_mutex is locked * before it is called. */ -static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_disc *disc) +static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp) { struct fc_lport *lport; struct fc_els_rscn *rp; @@ -151,7 +149,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, break; } } - lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); + lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL); /* * If not doing a complete rediscovery, do GPN_ID on @@ -177,25 +175,22 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, return; reject: FC_DISC_DBG(disc, "Received a bad RSCN frame\n"); - rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_LOGIC; rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data); fc_frame_free(fp); } /** * fc_disc_recv_req() - Handle incoming requests - * @sp: The sequence of the request exchange - * @fp: The request frame * @lport: The local port receiving the request + * @fp: The request frame * * Locking Note: This function is called from the EM and will lock * the disc_mutex before calling the handler for the * request. */ -static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lport) +static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp) { u8 op; struct fc_disc *disc = &lport->disc; @@ -204,7 +199,7 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp, switch (op) { case ELS_RSCN: mutex_lock(&disc->disc_mutex); - fc_disc_recv_rscn_req(sp, fp, disc); + fc_disc_recv_rscn_req(disc, fp); mutex_unlock(&disc->disc_mutex); break; default: diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 027042a6de3b..b8560ad8cf66 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -129,11 +129,11 @@ struct fc_exch_mgr_anchor { }; static void fc_exch_rrq(struct fc_exch *); -static void fc_seq_ls_acc(struct fc_seq *); -static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason, +static void fc_seq_ls_acc(struct fc_frame *); +static void fc_seq_ls_rjt(struct fc_frame *, enum fc_els_rjt_reason, enum fc_els_rjt_explan); -static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *); -static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *); +static void fc_exch_els_rec(struct fc_frame *); +static void fc_exch_els_rrq(struct fc_frame *); /* * Internal implementation notes. @@ -1003,28 +1003,30 @@ static void fc_exch_set_addr(struct fc_exch *ep, /** * fc_seq_els_rsp_send() - Send an ELS response using infomation from * the existing sequence/exchange. - * @sp: The sequence/exchange to get information from + * @fp: The received frame * @els_cmd: The ELS command to be sent * @els_data: The ELS data to be sent + * + * The received frame is not freed. */ -static void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd, +static void fc_seq_els_rsp_send(struct fc_frame *fp, enum fc_els_cmd els_cmd, struct fc_seq_els_data *els_data) { switch (els_cmd) { case ELS_LS_RJT: - fc_seq_ls_rjt(sp, els_data->reason, els_data->explan); + fc_seq_ls_rjt(fp, els_data->reason, els_data->explan); break; case ELS_LS_ACC: - fc_seq_ls_acc(sp); + fc_seq_ls_acc(fp); break; case ELS_RRQ: - fc_exch_els_rrq(sp, els_data->fp); + fc_exch_els_rrq(fp); break; case ELS_REC: - fc_exch_els_rec(sp, els_data->fp); + fc_exch_els_rec(fp); break; default: - FC_EXCH_DBG(fc_seq_exch(sp), "Invalid ELS CMD:%x\n", els_cmd); + FC_LPORT_DBG(fr_dev(fp), "Invalid ELS CMD:%x\n", els_cmd); } } @@ -1253,11 +1255,13 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) } /** - * fc_exch_recv_req() - Handler for an incoming request where is other - * end is originating the sequence + * fc_exch_recv_req() - Handler for an incoming request * @lport: The local port that received the request * @mp: The EM that the exchange is on * @fp: The request frame + * + * This is used when the other end is originating the exchange + * and the sequence. */ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp, struct fc_frame *fp) @@ -1275,8 +1279,17 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp, fc_frame_free(fp); return; } + fr_dev(fp) = lport; + + BUG_ON(fr_seq(fp)); /* XXX remove later */ + + /* + * If the RX_ID is 0xffff, don't allocate an exchange. + * The upper-level protocol may request one later, if needed. + */ + if (fh->fh_rx_id == htons(FC_XID_UNKNOWN)) + return lport->tt.lport_recv(lport, fp); - fr_seq(fp) = NULL; reject = fc_seq_lookup_recip(lport, mp, fp); if (reject == FC_RJT_NONE) { sp = fr_seq(fp); /* sequence will be held */ @@ -1298,7 +1311,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp, if (ep->resp) ep->resp(sp, fp, ep->arg); else - lport->tt.lport_recv(lport, sp, fp); + lport->tt.lport_recv(lport, fp); fc_exch_release(ep); /* release from lookup */ } else { FC_LPORT_DBG(lport, "exch/seq lookup failed: reject %x\n", @@ -1566,53 +1579,55 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp) /** * fc_seq_ls_acc() - Accept sequence with LS_ACC - * @req_sp: The request sequence + * @rx_fp: The received frame, not freed here. * * If this fails due to allocation or transmit congestion, assume the * originator will repeat the sequence. */ -static void fc_seq_ls_acc(struct fc_seq *req_sp) +static void fc_seq_ls_acc(struct fc_frame *rx_fp) { - struct fc_seq *sp; + struct fc_lport *lport; struct fc_els_ls_acc *acc; struct fc_frame *fp; - sp = fc_seq_start_next(req_sp); - fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc)); - if (fp) { - acc = fc_frame_payload_get(fp, sizeof(*acc)); - memset(acc, 0, sizeof(*acc)); - acc->la_cmd = ELS_LS_ACC; - fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - } + lport = fr_dev(rx_fp); + fp = fc_frame_alloc(lport, sizeof(*acc)); + if (!fp) + return; + acc = fc_frame_payload_get(fp, sizeof(*acc)); + memset(acc, 0, sizeof(*acc)); + acc->la_cmd = ELS_LS_ACC; + fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); + lport->tt.frame_send(lport, fp); } /** * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT - * @req_sp: The request sequence + * @rx_fp: The received frame, not freed here. * @reason: The reason the sequence is being rejected - * @explan: The explaination for the rejection + * @explan: The explanation for the rejection * * If this fails due to allocation or transmit congestion, assume the * originator will repeat the sequence. */ -static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason, +static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason, enum fc_els_rjt_explan explan) { - struct fc_seq *sp; + struct fc_lport *lport; struct fc_els_ls_rjt *rjt; struct fc_frame *fp; - sp = fc_seq_start_next(req_sp); - fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*rjt)); - if (fp) { - rjt = fc_frame_payload_get(fp, sizeof(*rjt)); - memset(rjt, 0, sizeof(*rjt)); - rjt->er_cmd = ELS_LS_RJT; - rjt->er_reason = reason; - rjt->er_explan = explan; - fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - } + lport = fr_dev(rx_fp); + fp = fc_frame_alloc(lport, sizeof(*rjt)); + if (!fp) + return; + rjt = fc_frame_payload_get(fp, sizeof(*rjt)); + memset(rjt, 0, sizeof(*rjt)); + rjt->er_cmd = ELS_LS_RJT; + rjt->er_reason = reason; + rjt->er_explan = explan; + fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); + lport->tt.frame_send(lport, fp); } /** @@ -1714,18 +1729,34 @@ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did) } EXPORT_SYMBOL(fc_exch_mgr_reset); +/** + * fc_exch_lookup() - find an exchange + * @lport: The local port + * @xid: The exchange ID + * + * Returns exchange pointer with hold for caller, or NULL if not found. + */ +static struct fc_exch *fc_exch_lookup(struct fc_lport *lport, u32 xid) +{ + struct fc_exch_mgr_anchor *ema; + + list_for_each_entry(ema, &lport->ema_list, ema_list) + if (ema->mp->min_xid <= xid && xid <= ema->mp->max_xid) + return fc_exch_find(ema->mp, xid); + return NULL; +} + /** * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests - * @sp: The sequence the REC is on - * @rfp: The REC frame + * @rfp: The REC frame, not freed here. * * Note that the requesting port may be different than the S_ID in the request. */ -static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp) +static void fc_exch_els_rec(struct fc_frame *rfp) { + struct fc_lport *lport; struct fc_frame *fp; struct fc_exch *ep; - struct fc_exch_mgr *em; struct fc_els_rec *rp; struct fc_els_rec_acc *acc; enum fc_els_rjt_reason reason = ELS_RJT_LOGIC; @@ -1734,6 +1765,7 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp) u16 rxid; u16 oxid; + lport = fr_dev(rfp); rp = fc_frame_payload_get(rfp, sizeof(*rp)); explan = ELS_EXPL_INV_LEN; if (!rp) @@ -1742,35 +1774,19 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp) rxid = ntohs(rp->rec_rx_id); oxid = ntohs(rp->rec_ox_id); - /* - * Currently it's hard to find the local S_ID from the exchange - * manager. This will eventually be fixed, but for now it's easier - * to lookup the subject exchange twice, once as if we were - * the initiator, and then again if we weren't. - */ - em = fc_seq_exch(sp)->em; - ep = fc_exch_find(em, oxid); + ep = fc_exch_lookup(lport, + sid == fc_host_port_id(lport->host) ? oxid : rxid); explan = ELS_EXPL_OXID_RXID; - if (ep && ep->oid == sid) { - if (ep->rxid != FC_XID_UNKNOWN && - rxid != FC_XID_UNKNOWN && - ep->rxid != rxid) - goto rel; - } else { - if (ep) - fc_exch_release(ep); - ep = NULL; - if (rxid != FC_XID_UNKNOWN) - ep = fc_exch_find(em, rxid); - if (!ep) - goto reject; - } - - fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc)); - if (!fp) { - fc_exch_done(sp); + if (!ep) + goto reject; + if (ep->oid != sid || oxid != ep->oxid) + goto rel; + if (rxid != FC_XID_UNKNOWN && rxid != ep->rxid) + goto rel; + fp = fc_frame_alloc(lport, sizeof(*acc)); + if (!fp) goto out; - } + acc = fc_frame_payload_get(fp, sizeof(*acc)); memset(acc, 0, sizeof(*acc)); acc->reca_cmd = ELS_LS_ACC; @@ -1785,18 +1801,16 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp) acc->reca_e_stat = htonl(ep->esb_stat & (ESB_ST_RESP | ESB_ST_SEQ_INIT | ESB_ST_COMPLETE)); - sp = fc_seq_start_next(sp); - fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); + fc_fill_reply_hdr(fp, rfp, FC_RCTL_ELS_REP, 0); + lport->tt.frame_send(lport, fp); out: fc_exch_release(ep); - fc_frame_free(rfp); return; rel: fc_exch_release(ep); reject: - fc_seq_ls_rjt(sp, reason, explan); - fc_frame_free(rfp); + fc_seq_ls_rjt(rfp, reason, explan); } /** @@ -1971,20 +1985,20 @@ retry: spin_unlock_bh(&ep->ex_lock); } - /** * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests - * @sp: The sequence that the RRQ is on - * @fp: The RRQ frame + * @fp: The RRQ frame, not freed here. */ -static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp) +static void fc_exch_els_rrq(struct fc_frame *fp) { + struct fc_lport *lport; struct fc_exch *ep = NULL; /* request or subject exchange */ struct fc_els_rrq *rp; u32 sid; u16 xid; enum fc_els_rjt_explan explan; + lport = fr_dev(fp); rp = fc_frame_payload_get(fp, sizeof(*rp)); explan = ELS_EXPL_INV_LEN; if (!rp) @@ -1993,11 +2007,10 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp) /* * lookup subject exchange. */ - ep = fc_seq_exch(sp); sid = ntoh24(rp->rrq_s_id); /* subject source */ - xid = ep->did == sid ? ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id); - ep = fc_exch_find(ep->em, xid); - + xid = fc_host_port_id(lport->host) == sid ? + ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id); + ep = fc_exch_lookup(lport, xid); explan = ELS_EXPL_OXID_RXID; if (!ep) goto reject; @@ -2028,15 +2041,14 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp) /* * Send LS_ACC. */ - fc_seq_ls_acc(sp); + fc_seq_ls_acc(fp); goto out; unlock_reject: spin_unlock_bh(&ep->ex_lock); reject: - fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan); + fc_seq_ls_rjt(fp, ELS_RJT_LOGIC, explan); out: - fc_frame_free(fp); if (ep) fc_exch_release(ep); /* drop hold from fc_exch_find */ } @@ -2267,7 +2279,7 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) fc_exch_recv_seq_resp(ema->mp, fp); else if (f_ctl & FC_FC_SEQ_CTX) fc_exch_recv_resp(ema->mp, fp); - else + else /* no EX_CTX and no SEQ_CTX */ fc_exch_recv_req(lport, ema->mp, fp); break; default: diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index e50a6606d4bf..1998c03634da 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -375,34 +375,31 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type) /** * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report. - * @sp: The sequence in the RLIR exchange - * @fp: The RLIR request frame * @lport: Fibre Channel local port recieving the RLIR + * @fp: The RLIR request frame * * Locking Note: The lport lock is expected to be held before calling * this function. */ -static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lport) +static void fc_lport_recv_rlir_req(struct fc_lport *lport, struct fc_frame *fp) { FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n", fc_lport_state(lport)); - lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); + lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL); fc_frame_free(fp); } /** * fc_lport_recv_echo_req() - Handle received ECHO request - * @sp: The sequence in the ECHO exchange - * @fp: ECHO request frame * @lport: The local port recieving the ECHO + * @fp: ECHO request frame * * Locking Note: The lport lock is expected to be held before calling * this function. */ -static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, - struct fc_lport *lport) +static void fc_lport_recv_echo_req(struct fc_lport *lport, + struct fc_frame *in_fp) { struct fc_frame *fp; unsigned int len; @@ -431,15 +428,14 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, /** * fc_lport_recv_rnid_req() - Handle received Request Node ID data request - * @sp: The sequence in the RNID exchange - * @fp: The RNID request frame * @lport: The local port recieving the RNID + * @fp: The RNID request frame * * Locking Note: The lport lock is expected to be held before calling * this function. */ -static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, - struct fc_lport *lport) +static void fc_lport_recv_rnid_req(struct fc_lport *lport, + struct fc_frame *in_fp) { struct fc_frame *fp; struct fc_els_rnid *req; @@ -457,10 +453,9 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, req = fc_frame_payload_get(in_fp, sizeof(*req)); if (!req) { - rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_LOGIC; rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data); } else { fmt = req->rnid_fmt; len = sizeof(*rp); @@ -492,17 +487,15 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, /** * fc_lport_recv_logo_req() - Handle received fabric LOGO request - * @sp: The sequence in the LOGO exchange - * @fp: The LOGO request frame * @lport: The local port recieving the LOGO + * @fp: The LOGO request frame * * Locking Note: The lport lock is exected to be held before calling * this function. */ -static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lport) +static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) { - lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); + lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL); fc_lport_enter_reset(lport); fc_frame_free(fp); } @@ -773,9 +766,8 @@ EXPORT_SYMBOL(fc_lport_set_local_id); /** * fc_lport_recv_flogi_req() - Receive a FLOGI request - * @sp_in: The sequence the FLOGI is on - * @rx_fp: The FLOGI frame * @lport: The local port that recieved the request + * @rx_fp: The FLOGI frame * * A received FLOGI request indicates a point-to-point connection. * Accept it with the common service parameters indicating our N port. @@ -784,13 +776,11 @@ EXPORT_SYMBOL(fc_lport_set_local_id); * Locking Note: The lport lock is expected to be held before calling * this function. */ -static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, - struct fc_frame *rx_fp, - struct fc_lport *lport) +static void fc_lport_recv_flogi_req(struct fc_lport *lport, + struct fc_frame *rx_fp) { struct fc_frame *fp; struct fc_frame_header *fh; - struct fc_seq *sp; struct fc_els_flogi *flp; struct fc_els_flogi *new_flp; u64 remote_wwpn; @@ -850,16 +840,13 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, } fc_lport_ptp_setup(lport, remote_fid, remote_wwpn, get_unaligned_be64(&flp->fl_wwnn)); - out: - sp = fr_seq(rx_fp); fc_frame_free(rx_fp); } /** * fc_lport_recv_req() - The generic lport request handler * @lport: The local port that received the request - * @sp: The sequence the request is on * @fp: The request frame * * This function will see if the lport handles the request or @@ -868,11 +855,10 @@ out: * Locking Note: This function should not be called with the lport * lock held becuase it will grab the lock. */ -static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp, - struct fc_frame *fp) +static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); - void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *); + void (*recv)(struct fc_lport *, struct fc_frame *); mutex_lock(&lport->lp_mutex); @@ -912,19 +898,13 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp, break; } - recv(sp, fp, lport); + recv(lport, fp); } else { FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n", fr_eof(fp)); fc_frame_free(fp); } mutex_unlock(&lport->lp_mutex); - - /* - * The common exch_done for all request may not be good - * if any request requires longer hold on exhange. XXX - */ - lport->tt.exch_done(sp); } /** diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 598795123211..25479cc7f170 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -68,14 +68,10 @@ static void fc_rport_enter_ready(struct fc_rport_priv *); static void fc_rport_enter_logo(struct fc_rport_priv *); static void fc_rport_enter_adisc(struct fc_rport_priv *); -static void fc_rport_recv_plogi_req(struct fc_lport *, - struct fc_seq *, struct fc_frame *); -static void fc_rport_recv_prli_req(struct fc_rport_priv *, - struct fc_seq *, struct fc_frame *); -static void fc_rport_recv_prlo_req(struct fc_rport_priv *, - struct fc_seq *, struct fc_frame *); -static void fc_rport_recv_logo_req(struct fc_lport *, - struct fc_seq *, struct fc_frame *); +static void fc_rport_recv_plogi_req(struct fc_lport *, struct fc_frame *); +static void fc_rport_recv_prli_req(struct fc_rport_priv *, struct fc_frame *); +static void fc_rport_recv_prlo_req(struct fc_rport_priv *, struct fc_frame *); +static void fc_rport_recv_logo_req(struct fc_lport *, struct fc_frame *); static void fc_rport_timeout(struct work_struct *); static void fc_rport_error(struct fc_rport_priv *, struct fc_frame *); static void fc_rport_error_retry(struct fc_rport_priv *, struct fc_frame *); @@ -736,11 +732,10 @@ static void fc_rport_enter_flogi(struct fc_rport_priv *rdata) /** * fc_rport_recv_flogi_req() - Handle Fabric Login (FLOGI) request in p-mp mode * @lport: The local port that received the PLOGI request - * @sp: The sequence that the PLOGI request was on * @rx_fp: The PLOGI request frame */ static void fc_rport_recv_flogi_req(struct fc_lport *lport, - struct fc_seq *sp, struct fc_frame *rx_fp) + struct fc_frame *rx_fp) { struct fc_disc *disc; struct fc_els_flogi *flp; @@ -749,7 +744,6 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport, struct fc_seq_els_data rjt_data; u32 sid; - rjt_data.fp = NULL; sid = fc_frame_sid(fp); FC_RPORT_ID_DBG(lport, sid, "Received FLOGI request\n"); @@ -817,7 +811,6 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport, if (!fp) goto out; - sp = lport->tt.seq_start_next(sp); fc_flogi_fill(lport, fp); flp = fc_frame_payload_get(fp, sizeof(*flp)); flp->fl_cmd = ELS_LS_ACC; @@ -837,7 +830,7 @@ out: reject: mutex_unlock(&disc->disc_mutex); - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); fc_frame_free(rx_fp); } @@ -1296,13 +1289,12 @@ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata) /** * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests * @rdata: The remote port that sent the ADISC request - * @sp: The sequence the ADISC request was on * @in_fp: The ADISC request frame * * Locking Note: Called with the lport and rport locks held. */ static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata, - struct fc_seq *sp, struct fc_frame *in_fp) + struct fc_frame *in_fp) { struct fc_lport *lport = rdata->local_port; struct fc_frame *fp; @@ -1313,10 +1305,9 @@ static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata, adisc = fc_frame_payload_get(in_fp, sizeof(*adisc)); if (!adisc) { - rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_PROT; rjt_data.explan = ELS_EXPL_INV_LEN; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data); goto drop; } @@ -1335,14 +1326,13 @@ drop: /** * fc_rport_recv_rls_req() - Handle received Read Link Status request * @rdata: The remote port that sent the RLS request - * @sp: The sequence that the RLS was on * @rx_fp: The PRLI request frame * * Locking Note: The rport lock is expected to be held before calling * this function. */ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata, - struct fc_seq *sp, struct fc_frame *rx_fp) + struct fc_frame *rx_fp) { struct fc_lport *lport = rdata->local_port; @@ -1393,8 +1383,7 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata, goto out; out_rjt: - rjt_data.fp = NULL; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); out: fc_frame_free(rx_fp); } @@ -1402,7 +1391,6 @@ out: /** * fc_rport_recv_els_req() - Handler for validated ELS requests * @lport: The local port that received the ELS request - * @sp: The sequence that the ELS request was on * @fp: The ELS request frame * * Handle incoming ELS requests that require port login. @@ -1410,16 +1398,11 @@ out: * * Locking Note: Called with the lport lock held. */ -static void fc_rport_recv_els_req(struct fc_lport *lport, - struct fc_seq *sp, struct fc_frame *fp) +static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp) { struct fc_rport_priv *rdata; struct fc_seq_els_data els_data; - els_data.fp = NULL; - els_data.reason = ELS_RJT_UNAB; - els_data.explan = ELS_EXPL_PLOGI_REQD; - mutex_lock(&lport->disc.disc_mutex); rdata = lport->tt.rport_lookup(lport, fc_frame_sid(fp)); if (!rdata) { @@ -1442,24 +1425,24 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, switch (fc_frame_payload_op(fp)) { case ELS_PRLI: - fc_rport_recv_prli_req(rdata, sp, fp); + fc_rport_recv_prli_req(rdata, fp); break; case ELS_PRLO: - fc_rport_recv_prlo_req(rdata, sp, fp); + fc_rport_recv_prlo_req(rdata, fp); break; case ELS_ADISC: - fc_rport_recv_adisc_req(rdata, sp, fp); + fc_rport_recv_adisc_req(rdata, fp); break; case ELS_RRQ: - els_data.fp = fp; - lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data); + lport->tt.seq_els_rsp_send(fp, ELS_RRQ, NULL); + fc_frame_free(fp); break; case ELS_REC: - els_data.fp = fp; - lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data); + lport->tt.seq_els_rsp_send(fp, ELS_REC, NULL); + fc_frame_free(fp); break; case ELS_RLS: - fc_rport_recv_rls_req(rdata, sp, fp); + fc_rport_recv_rls_req(rdata, fp); break; default: fc_frame_free(fp); /* can't happen */ @@ -1470,20 +1453,20 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, return; reject: - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); + els_data.reason = ELS_RJT_UNAB; + els_data.explan = ELS_EXPL_PLOGI_REQD; + lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &els_data); fc_frame_free(fp); } /** * fc_rport_recv_req() - Handler for requests - * @sp: The sequence the request was on - * @fp: The request frame * @lport: The local port that received the request + * @fp: The request frame * * Locking Note: Called with the lport lock held. */ -void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lport) +void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp) { struct fc_seq_els_data els_data; @@ -1495,13 +1478,13 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, */ switch (fc_frame_payload_op(fp)) { case ELS_FLOGI: - fc_rport_recv_flogi_req(lport, sp, fp); + fc_rport_recv_flogi_req(lport, fp); break; case ELS_PLOGI: - fc_rport_recv_plogi_req(lport, sp, fp); + fc_rport_recv_plogi_req(lport, fp); break; case ELS_LOGO: - fc_rport_recv_logo_req(lport, sp, fp); + fc_rport_recv_logo_req(lport, fp); break; case ELS_PRLI: case ELS_PRLO: @@ -1509,14 +1492,13 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, case ELS_RRQ: case ELS_REC: case ELS_RLS: - fc_rport_recv_els_req(lport, sp, fp); + fc_rport_recv_els_req(lport, fp); break; default: - fc_frame_free(fp); - els_data.fp = NULL; els_data.reason = ELS_RJT_UNSUP; els_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); + lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &els_data); + fc_frame_free(fp); break; } } @@ -1524,13 +1506,12 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, /** * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests * @lport: The local port that received the PLOGI request - * @sp: The sequence that the PLOGI request was on * @rx_fp: The PLOGI request frame * * Locking Note: The rport lock is held before calling this function. */ static void fc_rport_recv_plogi_req(struct fc_lport *lport, - struct fc_seq *sp, struct fc_frame *rx_fp) + struct fc_frame *rx_fp) { struct fc_disc *disc; struct fc_rport_priv *rdata; @@ -1539,7 +1520,6 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport, struct fc_seq_els_data rjt_data; u32 sid; - rjt_data.fp = NULL; sid = fc_frame_sid(fp); FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n"); @@ -1635,21 +1615,20 @@ out: return; reject: - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data); fc_frame_free(fp); } /** * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests * @rdata: The remote port that sent the PRLI request - * @sp: The sequence that the PRLI was on * @rx_fp: The PRLI request frame * * Locking Note: The rport lock is exected to be held before calling * this function. */ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, - struct fc_seq *sp, struct fc_frame *rx_fp) + struct fc_frame *rx_fp) { struct fc_lport *lport = rdata->local_port; struct fc_frame *fp; @@ -1666,7 +1645,6 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, u32 fcp_parm; u32 roles = FC_RPORT_ROLE_UNKNOWN; - rjt_data.fp = NULL; FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n", fc_rport_state(rdata)); @@ -1759,7 +1737,7 @@ reject_len: rjt_data.reason = ELS_RJT_PROT; rjt_data.explan = ELS_EXPL_INV_LEN; reject: - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); drop: fc_frame_free(rx_fp); } @@ -1767,18 +1745,15 @@ drop: /** * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests * @rdata: The remote port that sent the PRLO request - * @sp: The sequence that the PRLO was on * @rx_fp: The PRLO request frame * * Locking Note: The rport lock is exected to be held before calling * this function. */ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, - struct fc_seq *sp, struct fc_frame *rx_fp) { struct fc_lport *lport = rdata->local_port; - struct fc_exch *ep; struct fc_frame *fp; struct { struct fc_els_prlo prlo; @@ -1790,8 +1765,6 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, unsigned int plen; struct fc_seq_els_data rjt_data; - rjt_data.fp = NULL; - FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n", fc_rport_state(rdata)); @@ -1814,8 +1787,6 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, goto reject; } - sp = lport->tt.seq_start_next(sp); - WARN_ON(!sp); pp = fc_frame_payload_get(fp, len); WARN_ON(!pp); memset(pp, 0, len); @@ -1829,17 +1800,15 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, fc_rport_enter_delete(rdata, RPORT_EV_LOGO); - ep = fc_seq_exch(sp); - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, - FC_TYPE_ELS, FC_FCTL_RESP, 0); - lport->tt.seq_send(lport, sp, fp); + fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); + lport->tt.frame_send(lport, fp); goto drop; reject_len: rjt_data.reason = ELS_RJT_PROT; rjt_data.explan = ELS_EXPL_INV_LEN; reject: - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); drop: fc_frame_free(rx_fp); } @@ -1847,20 +1816,17 @@ drop: /** * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests * @lport: The local port that received the LOGO request - * @sp: The sequence that the LOGO request was on * @fp: The LOGO request frame * * Locking Note: The rport lock is exected to be held before calling * this function. */ -static void fc_rport_recv_logo_req(struct fc_lport *lport, - struct fc_seq *sp, - struct fc_frame *fp) +static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) { struct fc_rport_priv *rdata; u32 sid; - lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); + lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL); sid = fc_frame_sid(fp); diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 605f1d7861a7..14be49b44e84 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -249,14 +249,12 @@ struct fcoe_dev_stats { /** * struct fc_seq_els_data - ELS data used for passing ELS specific responses - * @fp: The ELS frame * @reason: The reason for rejection * @explan: The explaination of the rejection * * Mainly used by the exchange manager layer. */ struct fc_seq_els_data { - struct fc_frame *fp; enum fc_els_rjt_reason reason; enum fc_els_rjt_explan explan; }; @@ -519,12 +517,11 @@ struct libfc_function_template { struct fc_frame *); /* - * Send an ELS response using infomation from a previous - * exchange and sequence. + * Send an ELS response using infomation from the received frame. * * STATUS: OPTIONAL */ - void (*seq_els_rsp_send)(struct fc_seq *, enum fc_els_cmd, + void (*seq_els_rsp_send)(struct fc_frame *, enum fc_els_cmd, struct fc_seq_els_data *); /* @@ -583,8 +580,7 @@ struct libfc_function_template { * * STATUS: OPTIONAL */ - void (*lport_recv)(struct fc_lport *, struct fc_seq *, - struct fc_frame *); + void (*lport_recv)(struct fc_lport *, struct fc_frame *); /* * Reset the local port. @@ -646,8 +642,7 @@ struct libfc_function_template { * * STATUS: OPTIONAL */ - void (*rport_recv_req)(struct fc_seq *, struct fc_frame *, - struct fc_lport *); + void (*rport_recv_req)(struct fc_lport *, struct fc_frame *); /* * lookup an rport by it's port ID. @@ -693,8 +688,7 @@ struct libfc_function_template { * * STATUS: OPTIONAL */ - void (*disc_recv_req)(struct fc_seq *, struct fc_frame *, - struct fc_lport *); + void (*disc_recv_req)(struct fc_lport *, struct fc_frame *); /* * Start discovery for a local port. -- cgit v1.2.3 From 54a5b21da9d4d3f58770da5d1c244db9724659ee Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Tue, 20 Jul 2010 15:21:17 -0700 Subject: [SCSI] fcoe: fix offload feature flag change from netdev Currently, when FCoE netdev feature flags are toggled by the LLD, lport's corresponding flags are not updated. This causes the fc_fcp to still try to offload the I/O. This patch adds support of NETDEV_FEAT_CHANGE event in fcoe netdev device notification callback so we can update the lport offload flags appropriately. Signed-off-by: Yi Zou Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 68 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 19 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index ab6ea60f2aee..cf9d718c731c 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -588,6 +588,50 @@ static int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type) return -EINVAL; } +/** + * fcoe_netdev_features_change - Updates the lport's offload flags based + * on the LLD netdev's FCoE feature flags + */ +static void fcoe_netdev_features_change(struct fc_lport *lport, + struct net_device *netdev) +{ + mutex_lock(&lport->lp_mutex); + + if (netdev->features & NETIF_F_SG) + lport->sg_supp = 1; + else + lport->sg_supp = 0; + + if (netdev->features & NETIF_F_FCOE_CRC) { + lport->crc_offload = 1; + FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n"); + } else { + lport->crc_offload = 0; + } + + if (netdev->features & NETIF_F_FSO) { + lport->seq_offload = 1; + lport->lso_max = netdev->gso_max_size; + FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n", + lport->lso_max); + } else { + lport->seq_offload = 0; + lport->lso_max = 0; + } + + if (netdev->fcoe_ddp_xid) { + lport->lro_enabled = 1; + lport->lro_xid = netdev->fcoe_ddp_xid; + FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n", + lport->lro_xid); + } else { + lport->lro_enabled = 0; + lport->lro_xid = 0; + } + + mutex_unlock(&lport->lp_mutex); +} + /** * fcoe_netdev_config() - Set up net devive for SW FCoE * @lport: The local port that is associated with the net device @@ -624,25 +668,8 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) return -EINVAL; /* offload features support */ - if (netdev->features & NETIF_F_SG) - lport->sg_supp = 1; + fcoe_netdev_features_change(lport, netdev); - if (netdev->features & NETIF_F_FCOE_CRC) { - lport->crc_offload = 1; - FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n"); - } - if (netdev->features & NETIF_F_FSO) { - lport->seq_offload = 1; - lport->lso_max = netdev->gso_max_size; - FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n", - lport->lso_max); - } - if (netdev->fcoe_ddp_xid) { - lport->lro_enabled = 1; - lport->lro_xid = netdev->fcoe_ddp_xid; - FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n", - lport->lro_xid); - } skb_queue_head_init(&port->fcoe_pending_queue); port->fcoe_pending_queue_active = 0; setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport); @@ -1861,6 +1888,9 @@ static int fcoe_device_notification(struct notifier_block *notifier, schedule_work(&port->destroy_work); goto out; break; + case NETDEV_FEAT_CHANGE: + fcoe_netdev_features_change(lport, netdev); + break; default: FCOE_NETDEV_DBG(netdev, "Unknown event %ld " "from netdev netlink\n", event); @@ -2056,8 +2086,8 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) rc = -ENODEV; goto out_putdev; } - list_del(&fcoe->list); fcoe_interface_cleanup(fcoe); + list_del(&fcoe->list); /* RTNL mutex is dropped by fcoe_if_destroy */ fcoe_if_destroy(fcoe->ctlr.lp); -- cgit v1.2.3 From cf4aebcafb44a8810af10006dd4a5fcfb07bb810 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Tue, 20 Jul 2010 15:21:22 -0700 Subject: [SCSI] Revert "[SCSI] fcoe: Fix using VLAN ID in creating lport's WWWN/WWPN" This reverts commit cc0136c2e9c10e889cb36e39710c0eb10707b396. That commit introduced vlan id info to WWPN but WWPN needs to remain static as an unique port identifier in the fabric, therefore variable fabric vlan id info doesn't need to be coded inside WWPN. After this revert, port arg to fcoe_wwn_from_mac is always zero but still leaving it as-is okay to later allow users to use NAA 2 scheme with this additional port arg. Note with this patch, existing zoning using WWPN would require re-zoning this time only and later no more re-zoning due to any vlan id changes. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index cf9d718c731c..ddd2ca27e003 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -647,7 +647,6 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) u64 wwnn, wwpn; struct fcoe_interface *fcoe; struct fcoe_port *port; - int vid = 0; /* Setup lport private data to point to fcoe softc */ port = lport_priv(lport); @@ -677,20 +676,12 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) fcoe_link_speed_update(lport); if (!lport->vport) { - /* - * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN: - * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0. - * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID - */ - if (netdev->priv_flags & IFF_802_1Q_VLAN) - vid = vlan_dev_vlan_id(netdev); - if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0); fc_set_wwnn(lport, wwnn); if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN)) wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, - 2, vid); + 2, 0); fc_set_wwpn(lport, wwpn); } -- cgit v1.2.3 From 240778e821f596a6954116107c5cc3456df84f81 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Tue, 20 Jul 2010 15:21:33 -0700 Subject: [SCSI] fcoe: remove check for zero fabric name This check prevents FCF selection in NPV mode due to zero fabric name in that case and in turn flogi fails. Though NPV mode should not have this zero and should be fixed there also but spec also does not require initiator to ensure that fabric name must be non-zero, therefore dropping this check to get flogi working in NPV mode. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 2c265fe9ab32..aa503d83092a 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -845,7 +845,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, } if (!fcf->fc_map || (fcf->fc_map & 0x10000)) return -EINVAL; - if (!fcf->switch_name || !fcf->fabric_name) + if (!fcf->switch_name) return -EINVAL; if (desc_mask) { LIBFCOE_FIP_DBG(fip, "adv missing descriptors mask %x\n", -- cgit v1.2.3