From 00f0254ed9b19164d416dc2e3c2e81eda55a6faf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 1 Oct 2010 13:55:52 -0700 Subject: [SCSI] libsas: fix definition of wideport, include local sas address To date libsas has only looked at the attached sas address when determining the formation of wide ports. The specification and some hardware expects that phys with different addresses will not form a wide port unless the local peer phys also match each other. Introduce a flag to select stricter behavior at sas_register_ha() time. The flag can be dropped once it is known that all libsas users expect the same behavior. Current drivers just initialize this field to zero and get the traditional behavior. Reported-by: Patrick Thomson Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- include/scsi/libsas.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 90ce527ecf3..8f6bb9c7f3e 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -361,6 +361,8 @@ struct sas_ha_struct { /* The class calls this to send a task for execution. */ int lldd_max_execute_num; int lldd_queue_size; + int strict_wide_ports; /* both sas_addr and attached_sas_addr must match + * their siblings when forming wide ports */ /* LLDD calls these to notify the class of an event. */ void (*notify_ha_event)(struct sas_ha_struct *, enum ha_event); -- cgit v1.2.3-70-g09d2 From 8eea2f55a65b9471276e78e5c86ddd19c4c0365c Mon Sep 17 00:00:00 2001 From: Eddie Wai Date: Tue, 23 Nov 2010 15:29:21 -0800 Subject: [SCSI] bnx2i: Added fix for NOP-Out response panic from unsolicited NOP-In The patch fixes the following situations where NOP-Out pkt is called for: - local unsolicited NOP-Out requests (requesting no NOP-In response) - local NOP-Out responses to unsolicited NOP-In requests kernel panic is observed due to double session spin_lock requests; one in the bnx2i_process_nopin_local_cmpl routine in bnx2i_hwi.c and the other in the iscsi_put_task routine in libiscsi.c The proposed fix is to export the currently static __iscsi_put_task() routine and have bnx2i call it directly instead of the iscsi_put_task() routine which holds the session spin lock. Signed-off-by: Eddie Wai Reviewed-by: Michael Chan Reviewed-by: Anil Veerabhadrappa Acked-by: Benjamin Li Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/bnx2i/bnx2i_hwi.c | 2 +- drivers/scsi/libiscsi.c | 3 ++- include/scsi/libiscsi.h | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index 16c76e0b231..32cf9306ffc 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -1513,7 +1513,7 @@ static void bnx2i_process_nopin_local_cmpl(struct iscsi_session *session, task = iscsi_itt_to_task(conn, nop_in->itt & ISCSI_NOP_IN_MSG_INDEX); if (task) - iscsi_put_task(task); + __iscsi_put_task(task); spin_unlock(&session->lock); } diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index c15fde808c3..7551abe4090 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -539,11 +539,12 @@ void __iscsi_get_task(struct iscsi_task *task) } EXPORT_SYMBOL_GPL(__iscsi_get_task); -static void __iscsi_put_task(struct iscsi_task *task) +void __iscsi_put_task(struct iscsi_task *task) { if (atomic_dec_and_test(&task->refcount)) iscsi_free_task(task); } +EXPORT_SYMBOL_GPL(__iscsi_put_task); void iscsi_put_task(struct iscsi_task *task) { diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index b81d969ddc6..68e951d79f0 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -419,6 +419,7 @@ extern struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *, itt_t); extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t); extern void iscsi_requeue_task(struct iscsi_task *task); extern void iscsi_put_task(struct iscsi_task *task); +extern void __iscsi_put_task(struct iscsi_task *task); extern void __iscsi_get_task(struct iscsi_task *task); extern void iscsi_complete_scsi_task(struct iscsi_task *task, uint32_t exp_cmdsn, uint32_t max_cmdsn); -- cgit v1.2.3-70-g09d2 From 05fee645e96e732a79ad083cab8ddd4efd108e2c Mon Sep 17 00:00:00 2001 From: john fastabend Date: Tue, 30 Nov 2010 16:18:49 -0800 Subject: [SCSI] libfc: remove tgt_flags from fc_fcp_pkt struct We can easily remove the tgt_flags from fc_fcp_pkt struct and use rpriv->tgt_flags directly where needed. Signed-off-by: John Fastabend Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_fcp.c | 5 ++--- include/scsi/libfc.h | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index a7d956ad16e..a36c652edbb 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -1150,8 +1150,9 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp, setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp); - if (fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) + if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED) fc_fcp_timer_set(fsp, rec_tov); + unlock: fc_fcp_unlock_pkt(fsp); return rc; @@ -1867,8 +1868,6 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs } put_cpu(); - fsp->tgt_flags = rpriv->flags; - init_timer(&fsp->timer); fsp->timer.data = (unsigned long)fsp; diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 5c4c1678f7b..3eb3915eb55 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -263,7 +263,6 @@ struct fc_seq_els_data { * struct fc_fcp_pkt - FCP request structure (one for each scsi_cmnd request) * @lp: The associated local port * @state: The state of the I/O - * @tgt_flags: Target's flags * @ref_cnt: Reference count * @scsi_pkt_lock: Lock to protect the SCSI packet (must be taken before the * host_lock if both are to be held at the same time) @@ -298,7 +297,6 @@ struct fc_fcp_pkt { /* Housekeeping information */ struct fc_lport *lp; u16 state; - u16 tgt_flags; atomic_t ref_cnt; spinlock_t scsi_pkt_lock; -- cgit v1.2.3-70-g09d2 From 794d98e77f5901ceded697f1633463e88f078038 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 30 Nov 2010 16:19:56 -0800 Subject: [SCSI] libfcoe: retry rejected FLOGI to another FCF if possible Switches using multiple-FCFs may reject FLOGI in order to balance the load between multiple FCFs. Even though the FCF was available, it may have more load at the point we actually send the FLOGI. If the FLOGI fails, select a different FCF if possible, among those with the same priority. If no other FCF is available, just deliver the reject to libfc for retry. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/libfcoe.c | 207 +++++++++++++++++++++++++++++++++++++------- include/scsi/libfcoe.h | 8 ++ 2 files changed, 185 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 826c260f795..c90622c28b6 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -54,6 +54,8 @@ MODULE_LICENSE("GPL v2"); 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 int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *); +static void fcoe_ctlr_select(struct fcoe_ctlr *); static void fcoe_ctlr_vn_start(struct fcoe_ctlr *); static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *); @@ -176,6 +178,7 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode) fip->mode = mode; INIT_LIST_HEAD(&fip->fcfs); mutex_init(&fip->ctlr_mutex); + spin_lock_init(&fip->ctlr_lock); 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); @@ -231,17 +234,31 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) EXPORT_SYMBOL(fcoe_ctlr_destroy); /** - * fcoe_ctlr_announce() - announce new selection + * fcoe_ctlr_announce() - announce new FCF selection * @fip: The FCoE controller * * Also sets the destination MAC for FCoE and control packets + * + * Called with neither ctlr_mutex nor ctlr_lock held. */ static void fcoe_ctlr_announce(struct fcoe_ctlr *fip) { - struct fcoe_fcf *sel = fip->sel_fcf; + struct fcoe_fcf *sel; + struct fcoe_fcf *fcf; + + mutex_lock(&fip->ctlr_mutex); + spin_lock_bh(&fip->ctlr_lock); + + kfree_skb(fip->flogi_req); + fip->flogi_req = NULL; + list_for_each_entry(fcf, &fip->fcfs, list) + fcf->flogi_sent = 0; + + spin_unlock_bh(&fip->ctlr_lock); + sel = fip->sel_fcf; if (sel && !compare_ether_addr(sel->fcf_mac, fip->dest_addr)) - return; + goto unlock; if (!is_zero_ether_addr(fip->dest_addr)) { printk(KERN_NOTICE "libfcoe: host%d: " "FIP Fibre-Channel Forwarder MAC %pM deselected\n", @@ -255,6 +272,8 @@ static void fcoe_ctlr_announce(struct fcoe_ctlr *fip) memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN); fip->map_dest = 0; } +unlock: + mutex_unlock(&fip->ctlr_mutex); } /** @@ -591,6 +610,9 @@ 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. + * + * This is called from the lower-level driver with spinlocks held, + * so we must not take a mutex here. */ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, struct sk_buff *skb) @@ -628,7 +650,15 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, switch (op) { case ELS_FLOGI: op = FIP_DT_FLOGI; - break; + if (fip->mode == FIP_MODE_VN2VN) + break; + spin_lock_bh(&fip->ctlr_lock); + kfree_skb(fip->flogi_req); + fip->flogi_req = skb; + fip->flogi_req_send = 1; + spin_unlock_bh(&fip->ctlr_lock); + schedule_work(&fip->timer_work); + return -EINPROGRESS; case ELS_FDISC: if (ntoh24(fh->fh_s_id)) return 0; @@ -1088,18 +1118,24 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) els_op = *(u8 *)(fh + 1); 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); + sub == FIP_SC_REP && fip->mode != FIP_MODE_VN2VN) { + if (els_op == ELS_LS_ACC) { + 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 (fip->flogi_oxid == ntohs(fh->fh_ox_id)) { + fip->flogi_oxid = FC_XID_UNKNOWN; + if (els_dtype == FIP_DT_FLOGI) + fcoe_ctlr_announce(fip); + } + } else if (els_dtype == FIP_DT_FLOGI && + !fcoe_ctlr_flogi_retry(fip)) + goto drop; /* retrying FLOGI so drop reject */ } if ((desc_cnt == 0) || ((els_op != ELS_LS_RJT) && @@ -1355,12 +1391,15 @@ drop: * * If there are conflicting advertisements, no FCF can be chosen. * + * If there is already a selected FCF, this will choose a better one or + * an equivalent one that hasn't already been sent a FLOGI. + * * Called with lock held. */ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) { struct fcoe_fcf *fcf; - struct fcoe_fcf *best = NULL; + struct fcoe_fcf *best = fip->sel_fcf; struct fcoe_fcf *first; first = list_first_entry(&fip->fcfs, struct fcoe_fcf, list); @@ -1377,6 +1416,8 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) "or FC-MAP\n"); return NULL; } + if (fcf->flogi_sent) + continue; if (!fcoe_ctlr_fcf_usable(fcf)) { LIBFCOE_FIP_DBG(fip, "FCF for fab %16.16llx " "map %x %svalid %savailable\n", @@ -1386,11 +1427,7 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) "" : "un"); continue; } - if (!best) { - best = fcf; - continue; - } - if (fcf->pri < best->pri) + if (!best || fcf->pri < best->pri || best->flogi_sent) best = fcf; } fip->sel_fcf = best; @@ -1403,6 +1440,121 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) } } +/** + * fcoe_ctlr_flogi_send_locked() - send FIP-encapsulated FLOGI to current FCF + * @fip: The FCoE controller + * + * Returns non-zero error if it could not be sent. + * + * Called with ctlr_mutex and ctlr_lock held. + * Caller must verify that fip->sel_fcf is not NULL. + */ +static int fcoe_ctlr_flogi_send_locked(struct fcoe_ctlr *fip) +{ + struct sk_buff *skb; + struct sk_buff *skb_orig; + struct fc_frame_header *fh; + int error; + + skb_orig = fip->flogi_req; + if (!skb_orig) + return -EINVAL; + + /* + * Clone and send the FLOGI request. If clone fails, use original. + */ + skb = skb_clone(skb_orig, GFP_ATOMIC); + if (!skb) { + skb = skb_orig; + fip->flogi_req = NULL; + } + fh = (struct fc_frame_header *)skb->data; + error = fcoe_ctlr_encaps(fip, fip->lp, FIP_DT_FLOGI, skb, + ntoh24(fh->fh_d_id)); + if (error) { + kfree_skb(skb); + return error; + } + fip->send(fip, skb); + fip->sel_fcf->flogi_sent = 1; + return 0; +} + +/** + * fcoe_ctlr_flogi_retry() - resend FLOGI request to a new FCF if possible + * @fip: The FCoE controller + * + * Returns non-zero error code if there's no FLOGI request to retry or + * no alternate FCF available. + */ +static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip) +{ + struct fcoe_fcf *fcf; + int error; + + mutex_lock(&fip->ctlr_mutex); + spin_lock_bh(&fip->ctlr_lock); + LIBFCOE_FIP_DBG(fip, "re-sending FLOGI - reselect\n"); + fcoe_ctlr_select(fip); + fcf = fip->sel_fcf; + if (!fcf || fcf->flogi_sent) { + kfree_skb(fip->flogi_req); + fip->flogi_req = NULL; + error = -ENOENT; + } else { + fcoe_ctlr_solicit(fip, NULL); + error = fcoe_ctlr_flogi_send_locked(fip); + } + spin_unlock_bh(&fip->ctlr_lock); + mutex_unlock(&fip->ctlr_mutex); + return error; +} + + +/** + * fcoe_ctlr_flogi_send() - Handle sending of FIP FLOGI. + * @fip: The FCoE controller that timed out + * + * Done here because fcoe_ctlr_els_send() can't get mutex. + * + * Called with ctlr_mutex held. The caller must not hold ctlr_lock. + */ +static void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip) +{ + struct fcoe_fcf *fcf; + + spin_lock_bh(&fip->ctlr_lock); + fcf = fip->sel_fcf; + if (!fcf || !fip->flogi_req_send) + goto unlock; + + LIBFCOE_FIP_DBG(fip, "sending FLOGI\n"); + + /* + * If this FLOGI is being sent due to a timeout retry + * to the same FCF as before, select a different FCF if possible. + */ + if (fcf->flogi_sent) { + LIBFCOE_FIP_DBG(fip, "sending FLOGI - reselect\n"); + fcoe_ctlr_select(fip); + fcf = fip->sel_fcf; + if (!fcf || fcf->flogi_sent) { + LIBFCOE_FIP_DBG(fip, "sending FLOGI - clearing\n"); + list_for_each_entry(fcf, &fip->fcfs, list) + fcf->flogi_sent = 0; + fcoe_ctlr_select(fip); + fcf = fip->sel_fcf; + } + } + if (fcf) { + fcoe_ctlr_flogi_send_locked(fip); + fip->flogi_req_send = 0; + } else /* XXX */ + LIBFCOE_FIP_DBG(fip, "No FCF selected - defer send\n"); +unlock: + spin_unlock_bh(&fip->ctlr_lock); +} + /** * fcoe_ctlr_timeout() - FIP timeout handler * @arg: The FCoE controller that timed out @@ -1455,15 +1607,10 @@ static void fcoe_ctlr_timer_work(struct work_struct *work) next_timer = fip->sel_time; } - if (sel != fcf) { - fcf = sel; /* the old FCF may have been freed */ - fcoe_ctlr_announce(fip); - if (sel) { - if (time_after(next_timer, fip->ctlr_ka_time)) - next_timer = fip->ctlr_ka_time; - } else - reset = 1; - } + if (sel && fip->flogi_req_send) + fcoe_ctlr_flogi_send(fip); + else if (!sel && fcf) + reset = 1; if (sel && !sel->fd_flags) { if (time_after_eq(jiffies, fip->ctlr_ka_time)) { diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 06f1b5a8ed1..feb6a94c90e 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -92,10 +92,12 @@ enum fip_state { * @timer_work: &work_struct for doing keep-alives and resets. * @recv_work: &work_struct for receiving FIP frames. * @fip_recv_list: list of received FIP frames. + * @flogi_req: clone of FLOGI request sent * @rnd_state: state for pseudo-random number generator. * @port_id: proposed or selected local-port ID. * @user_mfs: configured maximum FC frame size, including FC header. * @flogi_oxid: exchange ID of most recent fabric login. + * @flogi_req_send: send of FLOGI requested * @flogi_count: number of FLOGI attempts in AUTO mode. * @map_dest: use the FC_MAP mode for destination MAC addresses. * @spma: supports SPMA server-provided MACs mode @@ -106,6 +108,7 @@ enum fip_state { * @update_mac: LLD-supplied function to handle changes to MAC addresses. * @get_src_addr: LLD-supplied function to supply a source MAC address. * @ctlr_mutex: lock protecting this structure. + * @ctlr_lock: spinlock covering flogi_req * * This structure is used by all FCoE drivers. It contains information * needed by all FCoE low-level drivers (LLDs) as well as internal state @@ -126,12 +129,14 @@ struct fcoe_ctlr { struct work_struct timer_work; struct work_struct recv_work; struct sk_buff_head fip_recv_list; + struct sk_buff *flogi_req; struct rnd_state rnd_state; u32 port_id; u16 user_mfs; u16 flogi_oxid; + u8 flogi_req_send; u8 flogi_count; u8 map_dest; u8 spma; @@ -143,6 +148,7 @@ struct fcoe_ctlr { void (*update_mac)(struct fc_lport *, u8 *addr); u8 * (*get_src_addr)(struct fc_lport *); struct mutex ctlr_mutex; + spinlock_t ctlr_lock; }; /** @@ -155,6 +161,7 @@ struct fcoe_ctlr { * @fcf_mac: Ethernet address of the FCF * @vfid: virtual fabric ID * @pri: selection priority, smaller values are better + * @flogi_sent: current FLOGI sent to this FCF * @flags: flags received from advertisement * @fka_period: keep-alive period, in jiffies * @@ -176,6 +183,7 @@ struct fcoe_fcf { u8 fcf_mac[ETH_ALEN]; u8 pri; + u8 flogi_sent; u16 flags; u32 fka_period; u8 fd_flags:1; -- cgit v1.2.3-70-g09d2 From 5f0e385fdafb7d6c8ded6464fa6421c735d96caf Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 30 Nov 2010 16:20:18 -0800 Subject: [SCSI] libfc: fix statistics for FCP input/output megabytes The statistics for InputMegabytes and OutputMegabytes are misnamed. They're accumulating bytes, not megabytes. The statistic returned via /sys must be in megabytes, however, which is what the HBA-API wants. The FCP code needs to accumulate it in bytes and then divide by 1,000,000 (not 2^20) before it presented via sysfs. This affects fcoe.ko only, not fnic. The fnic driver correctly by accumulating bytes and then converts to megabytes. I checked that libhbalinux is using the /sys file directly without conversion. BTW, qla2xxx does divide by 2^20, which I'm not fixing here. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_fcp.c | 4 ++-- drivers/scsi/libfc/fc_lport.c | 8 ++++++-- include/scsi/libfc.h | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index a8e0c0acc73..cdc06cda76e 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -1860,11 +1860,11 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) { fsp->req_flags = FC_SRB_READ; stats->InputRequests++; - stats->InputMegabytes += fsp->data_len; + stats->InputBytes += fsp->data_len; } else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { fsp->req_flags = FC_SRB_WRITE; stats->OutputRequests++; - stats->OutputMegabytes += fsp->data_len; + stats->OutputBytes += fsp->data_len; } else { fsp->req_flags = 0; stats->ControlRequests++; diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index b91a11e4fa0..c5a10f94f84 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -288,6 +288,8 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost) struct fc_lport *lport = shost_priv(shost); struct timespec v0, v1; unsigned int cpu; + u64 fcp_in_bytes = 0; + u64 fcp_out_bytes = 0; fcoe_stats = &lport->host_stats; memset(fcoe_stats, 0, sizeof(struct fc_host_statistics)); @@ -310,10 +312,12 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost) fcoe_stats->fcp_input_requests += stats->InputRequests; fcoe_stats->fcp_output_requests += stats->OutputRequests; fcoe_stats->fcp_control_requests += stats->ControlRequests; - fcoe_stats->fcp_input_megabytes += stats->InputMegabytes; - fcoe_stats->fcp_output_megabytes += stats->OutputMegabytes; + fcp_in_bytes += stats->InputBytes; + fcp_out_bytes += stats->OutputBytes; fcoe_stats->link_failure_count += stats->LinkFailureCount; } + fcoe_stats->fcp_input_megabytes = div_u64(fcp_in_bytes, 1000000); + fcoe_stats->fcp_output_megabytes = div_u64(fcp_out_bytes, 1000000); fcoe_stats->lip_count = -1; fcoe_stats->nos_count = -1; fcoe_stats->loss_of_sync_count = -1; diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 3eb3915eb55..f53c8e31d5f 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -221,8 +221,8 @@ struct fc_rport_priv { * @InputRequests: Number of input requests * @OutputRequests: Number of output requests * @ControlRequests: Number of control requests - * @InputMegabytes: Number of received megabytes - * @OutputMegabytes: Number of transmitted megabytes + * @InputBytes: Number of received bytes + * @OutputBytes: Number of transmitted bytes * @VLinkFailureCount: Number of virtual link failures * @MissDiscAdvCount: Number of missing FIP discovery advertisement */ @@ -241,8 +241,8 @@ struct fcoe_dev_stats { u64 InputRequests; u64 OutputRequests; u64 ControlRequests; - u64 InputMegabytes; - u64 OutputMegabytes; + u64 InputBytes; + u64 OutputBytes; u64 VLinkFailureCount; u64 MissDiscAdvCount; }; -- cgit v1.2.3-70-g09d2 From e9ccc998b70fbe59626f393bb0328402159c6b5c Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 17 Dec 2010 13:11:16 -0800 Subject: [SCSI] Add missing SPC-4 CDB and MAINTENANCE_[IN,OUT] service action definitions This patch adds a handful of missing CDBs defs that are used by TCM persistent reservation logic in the SPC-4 defined CDB exclusion table for registrations and reservations. This includes a number of missing MI_* and MO_* prefixed service actions defs for MAINTENANCE_IN and MAINTENANCE_OUT that are mentioned wrt to persistent registration and reservation status for the SCSI Initiator Port. Signed-off-by: Nicholas A. Bellinger Signed-off-by: James Bottomley --- include/scsi/scsi.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include') diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 216af8538cc..1651fef1883 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -115,33 +115,61 @@ struct scsi_cmnd; #define PERSISTENT_RESERVE_OUT 0x5f #define VARIABLE_LENGTH_CMD 0x7f #define REPORT_LUNS 0xa0 +#define SECURITY_PROTOCOL_IN 0xa2 #define MAINTENANCE_IN 0xa3 #define MAINTENANCE_OUT 0xa4 #define MOVE_MEDIUM 0xa5 #define EXCHANGE_MEDIUM 0xa6 #define READ_12 0xa8 #define WRITE_12 0xaa +#define READ_MEDIA_SERIAL_NUMBER 0xab #define WRITE_VERIFY_12 0xae #define VERIFY_12 0xaf #define SEARCH_HIGH_12 0xb0 #define SEARCH_EQUAL_12 0xb1 #define SEARCH_LOW_12 0xb2 +#define SECURITY_PROTOCOL_OUT 0xb5 #define READ_ELEMENT_STATUS 0xb8 #define SEND_VOLUME_TAG 0xb6 #define WRITE_LONG_2 0xea +#define EXTENDED_COPY 0x83 +#define RECEIVE_COPY_RESULTS 0x84 +#define ACCESS_CONTROL_IN 0x86 +#define ACCESS_CONTROL_OUT 0x87 #define READ_16 0x88 #define WRITE_16 0x8a +#define READ_ATTRIBUTE 0x8c +#define WRITE_ATTRIBUTE 0x8d #define VERIFY_16 0x8f #define WRITE_SAME_16 0x93 #define SERVICE_ACTION_IN 0x9e /* values for service action in */ #define SAI_READ_CAPACITY_16 0x10 #define SAI_GET_LBA_STATUS 0x12 +/* values for VARIABLE_LENGTH_CMD service action codes + * see spc4r17 Section D.3.5, table D.7 and D.8 */ +#define VLC_SA_RECEIVE_CREDENTIAL 0x1800 /* values for maintenance in */ +#define MI_REPORT_IDENTIFYING_INFORMATION 0x05 #define MI_REPORT_TARGET_PGS 0x0a +#define MI_REPORT_ALIASES 0x0b +#define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c +#define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d +#define MI_REPORT_PRIORITY 0x0e +#define MI_REPORT_TIMESTAMP 0x0f +#define MI_MANAGEMENT_PROTOCOL_IN 0x10 /* values for maintenance out */ +#define MO_SET_IDENTIFYING_INFORMATION 0x06 #define MO_SET_TARGET_PGS 0x0a +#define MO_CHANGE_ALIASES 0x0b +#define MO_SET_PRIORITY 0x0e +#define MO_SET_TIMESTAMP 0x0f +#define MO_MANAGEMENT_PROTOCOL_OUT 0x10 /* values for variable length command */ +#define XDREAD_32 0x03 +#define XDWRITE_32 0x04 +#define XPWRITE_32 0x06 +#define XDWRITEREAD_32 0x07 #define READ_32 0x09 #define VERIFY_32 0x0a #define WRITE_32 0x0b -- cgit v1.2.3-70-g09d2 From df4da5cdfca05b37b366b9c69925b68586969c1c Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 31 Dec 2010 02:22:18 -0600 Subject: [SCSI] libiscsi: add more informative failure message during iscsi scsi eh This adds a more informative error code and message for the iscsi scsi eh session drop paths. This allows you to distinguish if the session was dropped due to a connection failure vs the iscsi layer dropping the session due to scsi eh failure processing. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 10 +++++----- include/scsi/iscsi_if.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 7551abe4090..3eddab0774e 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1796,9 +1796,9 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, NULL, 0); if (!task) { spin_unlock_bh(&session->lock); + iscsi_conn_printk(KERN_ERR, conn, "Could not send TMF.\n"); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); spin_lock_bh(&session->lock); - ISCSI_DBG_EH(session, "tmf exec failure\n"); return -EPERM; } conn->tmfcmd_pdus_cnt++; @@ -2203,7 +2203,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) goto success_unlocked; case TMF_TIMEDOUT: spin_unlock_bh(&session->lock); - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); goto failed_unlocked; case TMF_NOT_FOUND: if (!sc->SCp.ptr) { @@ -2290,7 +2290,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) break; case TMF_TIMEDOUT: spin_unlock_bh(&session->lock); - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); goto done; default: conn->tmf_state = TMF_INITIAL; @@ -2371,7 +2371,7 @@ failed: * we drop the lock here but the leadconn cannot be destoyed while * we are in the scsi eh */ - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); ISCSI_DBG_EH(session, "wait for relogin\n"); wait_event_interruptible(conn->ehwait, @@ -2453,7 +2453,7 @@ int iscsi_eh_target_reset(struct scsi_cmnd *sc) break; case TMF_TIMEDOUT: spin_unlock_bh(&session->lock); - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); goto done; default: conn->tmf_state = TMF_INITIAL; diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index a8631acd37c..c3e1cbcc2ad 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -263,6 +263,7 @@ enum iscsi_err { ISCSI_ERR_INVALID_HOST = ISCSI_ERR_BASE + 18, ISCSI_ERR_XMIT_FAILED = ISCSI_ERR_BASE + 19, ISCSI_ERR_TCP_CONN_CLOSE = ISCSI_ERR_BASE + 20, + ISCSI_ERR_SCSI_EH_SESSION_RST = ISCSI_ERR_BASE + 21, }; /* -- cgit v1.2.3-70-g09d2 From f41d472179a0d7c8e8160c85180ab1124947068e Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 31 Dec 2010 02:22:21 -0600 Subject: [SCSI] libiscsi: do not take host lock in queuecommand iscsi_tcp, ib_iser, cxgb*, be2iscsi and bnx2i do not use the host lock and do not take the session lock against a irq, so this patch drops the DEF_SCSI_QCMD use. Instead we just take the session lock and disable bhs. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 37 ++++++++++++++----------------------- include/scsi/libiscsi.h | 3 ++- 2 files changed, 16 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 3eddab0774e..1def8e10124 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -505,6 +505,7 @@ static void iscsi_free_task(struct iscsi_task *task) struct iscsi_conn *conn = task->conn; struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = task->sc; + int oldstate = task->state; ISCSI_DBG_SESSION(session, "freeing task itt 0x%x state %d sc %p\n", task->itt, task->state, task->sc); @@ -525,10 +526,10 @@ static void iscsi_free_task(struct iscsi_task *task) /* SCSI eh reuses commands to verify us */ sc->SCp.ptr = NULL; /* - * queue command may call this to free the task, but - * not have setup the sc callback + * queue command may call this to free the task, so + * it will decide how to return sc to scsi-ml. */ - if (sc->scsi_done) + if (oldstate != ISCSI_TASK_REQUEUE_SCSIQ) sc->scsi_done(sc); } } @@ -572,7 +573,8 @@ static void iscsi_complete_task(struct iscsi_task *task, int state) task->itt, task->state, task->sc); if (task->state == ISCSI_TASK_COMPLETED || task->state == ISCSI_TASK_ABRT_TMF || - task->state == ISCSI_TASK_ABRT_SESS_RECOV) + task->state == ISCSI_TASK_ABRT_SESS_RECOV || + task->state == ISCSI_TASK_REQUEUE_SCSIQ) return; WARN_ON_ONCE(task->state == ISCSI_TASK_FREE); task->state = state; @@ -1600,27 +1602,23 @@ enum { FAILURE_SESSION_NOT_READY, }; -static int iscsi_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) +int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) { struct iscsi_cls_session *cls_session; - struct Scsi_Host *host; struct iscsi_host *ihost; int reason = 0; struct iscsi_session *session; struct iscsi_conn *conn; struct iscsi_task *task = NULL; - sc->scsi_done = done; sc->result = 0; sc->SCp.ptr = NULL; - host = sc->device->host; ihost = shost_priv(host); - spin_unlock(host->host_lock); cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; - spin_lock(&session->lock); + spin_lock_bh(&session->lock); reason = iscsi_session_chkready(cls_session); if (reason) { @@ -1706,25 +1704,21 @@ static int iscsi_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi } session->queued_cmdsn++; - spin_unlock(&session->lock); - spin_lock(host->host_lock); + spin_unlock_bh(&session->lock); return 0; prepd_reject: - sc->scsi_done = NULL; - iscsi_complete_task(task, ISCSI_TASK_COMPLETED); + iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); reject: - spin_unlock(&session->lock); + spin_unlock_bh(&session->lock); ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason); - spin_lock(host->host_lock); return SCSI_MLQUEUE_TARGET_BUSY; prepd_fault: - sc->scsi_done = NULL; - iscsi_complete_task(task, ISCSI_TASK_COMPLETED); + iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); fault: - spin_unlock(&session->lock); + spin_unlock_bh(&session->lock); ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason); if (!scsi_bidi_cmnd(sc)) @@ -1733,12 +1727,9 @@ fault: scsi_out(sc)->resid = scsi_out(sc)->length; scsi_in(sc)->resid = scsi_in(sc)->length; } - done(sc); - spin_lock(host->host_lock); + sc->scsi_done(sc); return 0; } - -DEF_SCSI_QCMD(iscsi_queuecommand) EXPORT_SYMBOL_GPL(iscsi_queuecommand); int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason) diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 68e951d79f0..748382b32b5 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -89,6 +89,7 @@ enum { ISCSI_TASK_RUNNING, ISCSI_TASK_ABRT_TMF, /* aborted due to TMF */ ISCSI_TASK_ABRT_SESS_RECOV, /* aborted due to session recovery */ + ISCSI_TASK_REQUEUE_SCSIQ, /* qcmd requeueing to scsi-ml */ }; struct iscsi_r2t_info { @@ -341,7 +342,7 @@ extern int iscsi_eh_abort(struct scsi_cmnd *sc); extern int iscsi_eh_recover_target(struct scsi_cmnd *sc); extern int iscsi_eh_session_reset(struct scsi_cmnd *sc); extern int iscsi_eh_device_reset(struct scsi_cmnd *sc); -extern int iscsi_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *sc); +extern int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc); /* * iSCSI host helpers. -- cgit v1.2.3-70-g09d2