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 50aaa4b..cc5e886 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.1 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 cc5e886..66120f1 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.1 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 66120f1..1d5b949 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.1 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 1d5b949..3ab3db3 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.1 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 3ab3db3..4893098 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.1 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 4893098..27c21ca 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.1 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 27c21ca..db7185c 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.1 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 db7185c..ce651ca 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.1 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 ce651ca..f009191 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.1 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 44a0759..d340cf2 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.1 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 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index d340cf2..a120962 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; } -- cgit v1.1 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 +++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 61 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index f009191..e510888 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; } -- cgit v1.1 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 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index a120962..9d64e08 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 e510888..76056e4 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; -- cgit v1.1 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 76056e4..11f3db5 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.1 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 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 1029 insertions(+), 62 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 9d64e08..216aba3 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 11f3db5..79df78f 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); } } @@ -1515,26 +1582,916 @@ 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); - -- cgit v1.1 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 79df78f..4865e81 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.1 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 216aba3..4d3e70a 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 4865e81..4de8ced 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -153,6 +153,20 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) } /** + * 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.1 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 4d3e70a..ab6ea60 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.1 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 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/scsi/fcoe') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 4de8ced..2c265fe 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); } -- cgit v1.1 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 ab6ea60..cf9d718 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -589,6 +589,50 @@ static int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type) } /** + * 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 * @netdev: The associated 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.1 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 cf9d718..ddd2ca2 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.1 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 2c265fe..aa503d8 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.1