From fc8fc7479c27768c3f6c74e4f7d696e12f1c7ec3 Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Thu, 8 Nov 2012 10:58:23 -0700 Subject: [PATCH] bnx2i: update to broadcom 2.7.4.1f driver Move from upstream to broadcom driver. Signed-off-by: Jerry Snitselaar --- drivers/scsi/bnx2i/57xx_iscsi_constants.h | 3 +- drivers/scsi/bnx2i/57xx_iscsi_hsi.h | 20 +- drivers/scsi/bnx2i/bnx2i.h | 176 ++++++- drivers/scsi/bnx2i/bnx2i_compat.h | 217 +++++++++ drivers/scsi/bnx2i/bnx2i_hwi.c | 228 +++++++-- drivers/scsi/bnx2i/bnx2i_init.c | 80 +++- drivers/scsi/bnx2i/bnx2i_ioctl.h | 47 ++ drivers/scsi/bnx2i/bnx2i_iscsi.c | 533 +++++++++++++++++++++- drivers/scsi/bnx2i/bnx2i_sysfs.c | 6 + 9 files changed, 1223 insertions(+), 87 deletions(-) create mode 100644 drivers/scsi/bnx2i/bnx2i_compat.h create mode 100644 drivers/scsi/bnx2i/bnx2i_ioctl.h diff --git a/drivers/scsi/bnx2i/57xx_iscsi_constants.h b/drivers/scsi/bnx2i/57xx_iscsi_constants.h index 25093a04123b..09c2c1021090 100644 --- a/drivers/scsi/bnx2i/57xx_iscsi_constants.h +++ b/drivers/scsi/bnx2i/57xx_iscsi_constants.h @@ -121,7 +121,8 @@ /* additional LOM specific iSCSI license not installed */ #define ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED (0x51) -#define ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY (0x80) +/* Driver internal error code */ +#define ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY (0x80) #define ISCSI_KCQE_COMPLETION_STATUS_PARITY_ERR (0x81) /* SQ/RQ/CQ DB structure sizes */ diff --git a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h index dc0a08e69c82..1f3f73117a07 100644 --- a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h +++ b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h @@ -267,7 +267,13 @@ struct bnx2i_cmd_request { * task statistics for write response */ struct bnx2i_write_resp_task_stat { - u32 num_data_ins; +#if defined(__BIG_ENDIAN) + u16 num_r2ts; + u16 num_data_outs; +#elif defined(__LITTLE_ENDIAN) + u16 num_data_outs; + u16 num_r2ts; +#endif }; /* @@ -275,11 +281,11 @@ struct bnx2i_write_resp_task_stat { */ struct bnx2i_read_resp_task_stat { #if defined(__BIG_ENDIAN) - u16 num_data_outs; - u16 num_r2ts; + u16 reserved; + u16 num_data_ins; #elif defined(__LITTLE_ENDIAN) - u16 num_r2ts; - u16 num_data_outs; + u16 num_data_ins; + u16 reserved; #endif }; @@ -543,11 +549,11 @@ struct iscsi_kwqe_header { struct iscsi_kwqe_init1 { #if defined(__BIG_ENDIAN) struct iscsi_kwqe_header hdr; - u8 reserved0; + u8 hsi_version; u8 num_cqs; #elif defined(__LITTLE_ENDIAN) u8 num_cqs; - u8 reserved0; + u8 hsi_version; struct iscsi_kwqe_header hdr; #endif u32 dummy_buffer_addr_lo; diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h index 14f086718e27..d76b9c64c00d 100644 --- a/drivers/scsi/bnx2i/bnx2i.h +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -36,15 +36,84 @@ #include #include #include -#include -#include -#include + +#include "bnx2i_compat.h" #include "../../net/cnic_if.h" + #include "57xx_iscsi_hsi.h" #include "57xx_iscsi_constants.h" +#include "../../net/bnx2x/bnx2x_mfw_req.h" + #define BNX2_ISCSI_DRIVER_NAME "bnx2i" +#ifndef PCI_DEVICE_ID_NX2_5709 +#define PCI_DEVICE_ID_NX2_5709 0x1639 +#endif + +#ifndef PCI_DEVICE_ID_NX2_5709S +#define PCI_DEVICE_ID_NX2_5709S 0x163a +#endif + +#ifndef PCI_DEVICE_ID_NX2_57710 +#define PCI_DEVICE_ID_NX2_57710 0x164e +#endif + +#ifndef PCI_DEVICE_ID_NX2_57711 +#define PCI_DEVICE_ID_NX2_57711 0x164f +#endif + +#ifndef PCI_DEVICE_ID_NX2_57711E +#define PCI_DEVICE_ID_NX2_57711E 0x1650 +#endif + +#ifndef PCI_DEVICE_ID_NX2_57712 +#define PCI_DEVICE_ID_NX2_57712 0x1662 +#endif + +#ifndef PCI_DEVICE_ID_NX2_57712_MF +#define PCI_DEVICE_ID_NX2_57712_MF 0x1663 +#endif + +#ifndef PCI_DEVICE_ID_NX2_57712_VF +#define PCI_DEVICE_ID_NX2_57712_VF 0x166f +#endif + +#ifndef PCI_DEVICE_ID_NX2_57800 +#define PCI_DEVICE_ID_NX2_57800 0x168a +#endif + +#ifndef PCI_DEVICE_ID_NX2_57800_MF +#define PCI_DEVICE_ID_NX2_57800_MF 0x16a5 +#endif + +#ifndef PCI_DEVICE_ID_NX2_57800_VF +#define PCI_DEVICE_ID_NX2_57800_VF 0x16a9 +#endif + +#ifndef PCI_DEVICE_ID_NX2_57810 +#define PCI_DEVICE_ID_NX2_57810 0x168e +#endif + +#ifndef PCI_DEVICE_ID_NX2_57810_MF +#define PCI_DEVICE_ID_NX2_57810_MF 0x16ae +#endif + +#ifndef PCI_DEVICE_ID_NX2_57810_VF +#define PCI_DEVICE_ID_NX2_57810_VF 0x16af +#endif + +#ifndef PCI_DEVICE_ID_NX2_57840 +#define PCI_DEVICE_ID_NX2_57840 0x168d +#endif + +#ifndef PCI_DEVICE_ID_NX2_57840_MF +#define PCI_DEVICE_ID_NX2_57840_MF 0x16ab +#endif + +#ifndef PCI_DEVICE_ID_NX2_57840_VF +#define PCI_DEVICE_ID_NX2_57840_VF 0x16ad +#endif #define BNX2I_MAX_ADAPTERS 8 @@ -126,6 +195,42 @@ #define REG_WR(__hba, offset, val) \ writel(val, __hba->regview + offset) +#ifdef CONFIG_32BIT +#define GET_STATS_64(__hba, dst, field) \ + do { \ + spin_lock_bh(&__hba->stat_lock); \ + dst->field##_lo = __hba->stats.field##_lo; \ + dst->field##_hi = __hba->stats.field##_hi; \ + spin_unlock_bh(&__hba->stat_lock); \ + } while (0) + +#define ADD_STATS_64(__hba, field, len) \ + do { \ + if (spin_trylock(&__hba->stat_lock)) { \ + if (__hba->stats.field##_lo + len < \ + __hba->stats.field##_lo) \ + __hba->stats.field##_hi++; \ + __hba->stats.field##_lo += len; \ + spin_unlock(&__hba->stat_lock); \ + } \ + } while (0) + +#else +#define GET_STATS_64(__hba, dst, field) \ + do { \ + u64 val, *out; \ + \ + val = __hba->bnx2i_stats.field; \ + out = (u64 *)&__hba->stats.field##_lo; \ + *out = cpu_to_le64(val); \ + out = (u64 *)&dst->field##_lo; \ + *out = cpu_to_le64(val); \ + } while (0) + +#define ADD_STATS_64(__hba, field, len) \ + __hba->bnx2i_stats.field += len + +#endif /** * struct generic_pdu_resc - login pdu resource structure @@ -212,6 +317,7 @@ struct io_bdt { * @io_tbl: buffer descriptor (BD) table * @bd_tbl_dma: buffer descriptor (BD) table's dma address * @req: bnx2i specific command request struct + * @cpu: CPU number of the specific cmd; for completion purposes */ struct bnx2i_cmd { struct iscsi_hdr hdr; @@ -221,6 +327,7 @@ struct bnx2i_cmd { struct io_bdt io_tbl; dma_addr_t bd_tbl_dma; struct bnx2i_cmd_request req; + u32 cpu; }; @@ -235,6 +342,7 @@ struct bnx2i_cmd { * @gen_pdu: login/nopout/logout pdu resources * @violation_notified: bit mask used to track iscsi error/warning messages * already printed out + * @prev_sess_state: keeps track of the connections previous session state * @work_cnt: keeps track of the number of outstanding work * * iSCSI connection structure @@ -260,11 +368,12 @@ struct bnx2i_conn { struct generic_pdu_resc gen_pdu; u64 violation_notified; + int prev_sess_state; + atomic_t work_cnt; }; - /** * struct iscsi_cid_queue - Per adapter iscsi cid queue * @@ -288,6 +397,29 @@ struct iscsi_cid_queue { struct bnx2i_conn **conn_cid_tbl; }; +struct bnx2i_stats_info { + u64 rx_pdus; + u64 rx_bytes; + u64 tx_pdus; + u64 tx_bytes; +}; + +struct iscsi_login_stats_info { + u32 successful_logins; /* Total login successes */ + u32 login_failures; /* Total login failures */ + u32 login_negotiation_failures; /* Text negotiation failed */ + u32 login_authentication_failures; /* login Authentication failed */ + u32 login_redirect_responses; /* Target redirects to another portal */ + u32 connection_timeouts; /* TCP connection timeouts */ + u32 session_failures; /* Errors resulting in sess recovery */ + u32 digest_errors; /* Errors resulting in digest errors */ +}; + +struct bnx2i_iface { + struct list_head link; + struct iscsi_iface *iface; +}; + /** * struct bnx2i_hba - bnx2i adapter structure * @@ -306,6 +438,11 @@ struct iscsi_cid_queue { * @max_sqes: SQ size * @max_rqes: RQ size * @max_cqes: CQ size + * @err_rec_task: error handling worker + * @conn_recov_list: conn list which are queued for recovery + * @conn_recov_prod_idx: producer index to manage conn recovery list + * @conn_recov_cons_idx: producer index to manage conn recovery list + * @conn_recov_max_idx: max index to manage conn recovery list * @num_ccell: number of command cells per connection * @ofld_conns_active: active connection list * @eh_wait: wait queue for the endpoint to shutdown @@ -341,6 +478,11 @@ struct iscsi_cid_queue { * @ctx_ccell_tasks: captures number of ccells and tasks supported by * currently offloaded connection, used to decode * context memory + * @stat_lock: statistic lock to maintain coherency + * @stats: iSCSI statistic structure memory + * @login_stats: iSCSI login statistic structure memory + * @iface_ipv4_list: bnx2i_iface struct list for IPv4 + * @iface_ipv6_list: bnx2i_iface struct list for IPv6 * * Adapter Data Structure */ @@ -378,6 +520,13 @@ struct bnx2i_hba { u32 max_sqes; u32 max_rqes; u32 max_cqes; + + struct work_struct err_rec_task; + struct iscsi_conn **conn_recov_list; + int conn_recov_prod_idx; + int conn_recov_cons_idx; + int conn_recov_max_idx; + u32 num_ccell; int ofld_conns_active; @@ -405,6 +554,7 @@ struct bnx2i_hba { int hba_shutdown_tmo; int conn_teardown_tmo; int conn_ctx_destroy_tmo; + /* * PCI related info. */ @@ -427,9 +577,20 @@ struct bnx2i_hba { u32 num_sess_opened; u32 num_conn_opened; unsigned int ctx_ccell_tasks; + +#ifdef CONFIG_32BIT + spinlock_t stat_lock; +#endif + struct bnx2i_stats_info bnx2i_stats; + struct iscsi_stats_info stats; + struct iscsi_login_stats_info login_stats; + + struct list_head iface_ipv4_list; + struct list_head iface_ipv6_list; }; + /******************************************************************************* * QP [ SQ / RQ / CQ ] info. ******************************************************************************/ @@ -727,8 +888,10 @@ struct bnx2i_percpu_s { extern unsigned int error_mask1, error_mask2; extern u64 iscsi_error_mask; extern unsigned int en_tcp_dack; +extern unsigned int time_stamps; extern unsigned int event_coal_div; extern unsigned int event_coal_min; +extern unsigned int tcp_buf_size; extern struct scsi_transport_template *bnx2i_scsi_xport_template; extern struct iscsi_transport bnx2i_iscsi_transport; @@ -736,6 +899,8 @@ extern struct cnic_ulp_ops bnx2i_cnic_cb; extern unsigned int sq_size; extern unsigned int rq_size; +extern unsigned int last_active_tcp_port; +extern unsigned int cmd_cmpl_per_work; extern struct device_attribute *bnx2i_dev_attributes[]; @@ -750,6 +915,8 @@ extern void bnx2i_ulp_init(struct cnic_dev *dev); extern void bnx2i_ulp_exit(struct cnic_dev *dev); extern void bnx2i_start(void *handle); extern void bnx2i_stop(void *handle); +extern int bnx2i_get_stats(void *handle); + extern struct bnx2i_hba *get_adapter_list_head(void); struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba, @@ -819,4 +986,5 @@ extern int bnx2i_percpu_io_thread(void *arg); extern int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session, struct bnx2i_conn *bnx2i_conn, struct cqe *cqe); +extern void bnx2i_add_stats(u32 *lo, u32 *hi, u32 length); #endif diff --git a/drivers/scsi/bnx2i/bnx2i_compat.h b/drivers/scsi/bnx2i/bnx2i_compat.h new file mode 100644 index 000000000000..469b84773bc0 --- /dev/null +++ b/drivers/scsi/bnx2i/bnx2i_compat.h @@ -0,0 +1,217 @@ +/* bnx2i_compat.h: Broadcom NetXtreme II iSCSI compatible header. + * + * Copyright (c) 2012 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: Eddie Wai (eddie.wai@broadcom.com) + */ + +#ifndef _BNX2I_COMPAT_H_ +#define _BNX2I_COMPAT_H_ + +/* Common */ +#define __RHELS_DISTRO__ \ + (defined(__RHELS_DISTRO_5__) || defined(__RHELS_DISTRO_6__)) + +#define __DISTRO__ \ + (defined(__SLES_DISTRO__) || defined(__RHELS_DISTRO__)) + +#if (defined(_EP_CONNECT_IFACE_NUM_)) +#define bnx2i_ep_connect(shost, dst_addr, non_blocking, iface_num) \ + bnx2i_ep_connect(shost, dst_addr, non_blocking, iface_num) + +#define bnx2i_offload_mesg(shost, transport, msg_type, buf, buflen, iface_num) \ + iscsi_offload_mesg(shost, transport, msg_type, buf, buflen, iface_num) +#else +#define bnx2i_ep_connect(shost, dst_addr, non_blocking, iface_num) \ + bnx2i_ep_connect(shost, dst_addr, non_blocking) + +#define bnx2i_offload_mesg(shost, transport, msg_type, buf, buflen, iface_num) \ + iscsi_offload_mesg(shost, transport, msg_type, buf, buflen) +#endif + + +#if (defined(__RHELS_DISTRO_5__)) +/********************************* RHEL 5.X ***********************************/ +/* Common for RHEL5 */ +#include +#include +#include +#include + +#define iscsi_create_endpoint(a) iscsi2_create_endpoint(a) +#define iscsi_destroy_endpoint(a) iscsi2_destroy_endpoint(a) +#define iscsi_session_failure(a,b) iscsi2_session_failure(a,b) +#define iscsi_host_alloc(a,b,c) iscsi2_host_alloc(a,b,c) +#define iscsi_host_add(a,b) iscsi2_host_add(a,b) +#define iscsi_host_for_each_session(a,b) iscsi2_host_for_each_session(a,b) +#define iscsi_host_remove(a) iscsi2_host_remove(a) +#define iscsi_host_free(a) iscsi2_host_free(a) +#if (__RHELS_DISTRO_5__ > 0x0504) +#define iscsi_session_setup(a,b,c,d,e,f,g) iscsi2_session_setup(a,b,c,d,e,f,g) +#else +#define iscsi_session_setup(a,b,c,d,e,f,g) iscsi2_session_setup(a,b,c,e,f,g) +#endif +#define iscsi_session_teardown(a) iscsi2_session_teardown(a) +#define iscsi_session_recovery_timedout iscsi2_session_recovery_timedout +#define iscsi_session_get_param iscsi2_session_get_param +#define iscsi_conn_setup(a,b,c) iscsi2_conn_setup(a,b,c) +#define iscsi_conn_bind(a,b,c) iscsi2_conn_bind(a,b,c) +#define iscsi_conn_start(a) iscsi2_conn_start(a) +#define iscsi_conn_send_pdu iscsi2_conn_send_pdu +#define iscsi_conn_stop iscsi2_conn_stop +#define iscsi_conn_failure(a,b) iscsi2_conn_failure(a,b) +#define iscsi_conn_teardown(a) iscsi2_conn_teardown(a) +#define iscsi_conn_error_event(a,b) iscsi2_conn_error_event(a,b) +#define iscsi_lookup_endpoint(a) iscsi2_lookup_endpoint(a) +#define iscsi_conn_get_param(a,b,c) iscsi2_conn_get_param(a,b,c) +#define iscsi_host_get_param(a,b,c) iscsi2_host_get_param(a,b,c) +#define iscsi_host_for_each_session(a,b) iscsi2_host_for_each_session(a,b) +#define iscsi_register_transport(a) iscsi2_register_transport(a) +#define iscsi_unregister_transport(a) iscsi2_unregister_transport(a) + +/* TODO: Setting the ISCSI_SUSPEND_BIT w/o bh lock! */ +#define iscsi_suspend_queue(a) iscsi2_suspend_tx(a) + +#define iscsi_queuecommand iscsi2_queuecommand +#define iscsi_eh_abort iscsi2_eh_abort +#define iscsi_eh_device_reset iscsi2_eh_device_reset +#define iscsi_change_queue_depth iscsi2_change_queue_depth + +#define iscsi_set_param iscsi2_set_param + +#define __iscsi_complete_pdu(a,b,c,d) __iscsi2_complete_pdu(a,b,c,d) +#define iscsi_put_task(a) iscsi2_put_task(a) + +static inline ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, + int len) +{ + int i; + char *cp = buf; + + for (i = 0; i < len; i++) + cp += sprintf(cp, "%02x%c", addr[i], + i == (len - 1) ? '\n' : ':'); + return cp - buf; +} + +#define FORMAT_IP(buf, fstr, src, len) \ + do { \ + u8 *ip = (u8 *)&src[0]; \ + len = sprintf(buf, "%d.%d.%d.%d", \ + ip[0], ip[1], ip[2], ip[3]); \ + } while (0) + +#define FORMAT_IP6(buf, fstr, src, len) \ + do { \ + u16 *ip = (u16 *)&src[0]; \ + len = sprintf(buf, "%04x:%04x:%04x:%04x:" \ + "%04x:%04x:%04x:%04x\n", \ + htons(ip[0]), htons(ip[1]), \ + htons(ip[2]), htons(ip[3]), \ + htons(ip[4]), htons(ip[5]), \ + htons(ip[6]), htons(ip[7])); \ + } while (0) + +#define scsi_for_each_sg(cmd, sg, nseg, __i) \ + for (__i = 0, sg = scsi_sglist(cmd); __i < (nseg); __i++, (sg)++) + +#define set_unfreezable(cur) \ + cur->flags |= PF_NOFREEZE; + +#define kthread_create_on_node(io_thread, arg, node, str, cpu) \ + kthread_create(io_thread, arg, str, cpu) + +/* RHEL5.x Version Specifics */ + +#if (__RHELS_DISTRO_5__ < 0x0508) +#define rounddown_pow_of_two(n) (roundup_pow_of_two(n) << 1) +#endif + +/* End of __RHELS_DISTRO_5__ */ + + +#elif (defined(__RHELS_DISTRO_6__)) +/********************************* RHEL 6.X ***********************************/ +/* Common for RHEL6 */ +#include +#include +#include +#include + +#define FORMAT_IP(buf, fstr, src, len) \ + do { \ + len = sprintf(buf, fstr, src); \ + } while (0) + +#define FORMAT_IP6(buf, fstr, src, len) FORMAT_IP(buf, fstr, src, len) + +#define kthread_create_on_node(io_thread, arg, node, str, cpu) \ + kthread_create(io_thread, arg, str, cpu) + +/* RHEL6.X Version Specifics */ +#define set_unfreezable(cur) + +/* End of __RHELS_DISTRO_6__ */ + + +#elif (defined(__SLES_DISTRO__)) +/********************************* SLES11SPX **********************************/ +/* Common for SLES11 */ +#include +#include +#include +#include + +#define FORMAT_IP(buf, fstr, src, len) \ + do { \ + len = sprintf(buf, fstr, src); \ + } while (0) + +#define FORMAT_IP6(buf, fstr, src, len) FORMAT_IP(buf, fstr, src, len) + +#define kthread_create_on_node(io_thread, arg, node, str, cpu) \ + kthread_create(io_thread, arg, str, cpu) + +#if (__SLES_DISTRO__ < 0x1102) +/* SLES11sp1 specific */ +#define set_unfreezable(cur) \ + cur->flags |= PF_NOFREEZE; +#else +/* SLES11sp2+ specific */ +#define iscsi_cmd iscsi_scsi_req +#define iscsi_cmd_rsp iscsi_scsi_rsp +#define iscsi_login iscsi_login_req + +#define set_unfreezable(cur) +#endif + +/* End of __SLES_DISTRO__ */ + +#else /* Upstream kernel */ +/********************************* Upstream **********************************/ +/* Common for all upstream kernels */ +#include +#include +#include +#include + +#define FORMAT_IP(buf, fstr, src, len) \ + do { \ + len = sprintf(buf, fstr, src); \ + } while (0) + +#define FORMAT_IP6(buf, fstr, src, len) FORMAT_IP(buf, fstr, src, len) + +#define kthread_create_on_node(io_thread, arg, node, str, cpu) \ + kthread_create(io_thread, arg, str, cpu) + +#define set_unfreezable(cur) + +#endif /* End of Upstream kernel */ + + +#endif /* _BNX2I_COMPAT_H_ */ diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index 970d184ecb9a..8d451bf54a21 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -14,7 +14,6 @@ #include #include -#include #include "bnx2i.h" DECLARE_PER_CPU(struct bnx2i_percpu_s, bnx2i_percpu); @@ -551,7 +550,7 @@ int bnx2i_send_iscsi_nopout(struct bnx2i_conn *bnx2i_conn, nopout_wqe->op_code = nopout_hdr->opcode; nopout_wqe->op_attr = ISCSI_FLAG_CMD_FINAL; - memcpy(nopout_wqe->lun, &nopout_hdr->lun, 8); + memcpy(nopout_wqe->lun, &nopout_hdr->lun, sizeof(struct scsi_lun)); if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) { u32 tmp = nopout_wqe->lun[0]; @@ -657,8 +656,11 @@ void bnx2i_update_iscsi_conn(struct iscsi_conn *conn) /* 5771x requires conn context id to be passed as is */ if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_conn->ep->hba->cnic_dev_type)) update_wqe->context_id = bnx2i_conn->ep->ep_cid; - else + else { update_wqe->context_id = (bnx2i_conn->ep->ep_cid >> 7); + /* Added for OOO l5_cid to context cid association */ + update_wqe->reserved2 = bnx2i_conn->ep->ep_iscsi_cid; + } update_wqe->conn_flags = 0; if (conn->hdrdgst_en) update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST; @@ -1273,7 +1275,6 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba) ISCSI_PAGE_SIZE_4K << ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT; if (en_tcp_dack) iscsi_init.flags |= ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE; - iscsi_init.reserved0 = 0; iscsi_init.num_cqs = 1; iscsi_init.hdr.op_code = ISCSI_KWQE_OPCODE_INIT1; iscsi_init.hdr.flags = @@ -1317,7 +1318,7 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba) (1ULL << ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN)); if (error_mask1) { iscsi_init2.error_bit_map[0] = error_mask1; - mask64 &= (u32)(~mask64); + mask64 ^= (u32) mask64; mask64 |= error_mask1; } else iscsi_init2.error_bit_map[0] = (u32) mask64; @@ -1353,6 +1354,7 @@ int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session, struct cqe *cqe) { struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; struct bnx2i_cmd_response *resp_cqe; struct bnx2i_cmd *bnx2i_cmd; struct iscsi_task *task; @@ -1370,16 +1372,26 @@ int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session, if (bnx2i_cmd->req.op_attr & ISCSI_CMD_REQUEST_READ) { conn->datain_pdus_cnt += - resp_cqe->task_stat.read_stat.num_data_outs; + resp_cqe->task_stat.read_stat.num_data_ins; conn->rxdata_octets += bnx2i_cmd->req.total_data_transfer_length; + ADD_STATS_64(hba, rx_pdus, + resp_cqe->task_stat.read_stat.num_data_ins); + ADD_STATS_64(hba, rx_bytes, + bnx2i_cmd->req.total_data_transfer_length); } else { conn->dataout_pdus_cnt += - resp_cqe->task_stat.read_stat.num_data_outs; + resp_cqe->task_stat.write_stat.num_data_outs; conn->r2t_pdus_cnt += - resp_cqe->task_stat.read_stat.num_r2ts; + resp_cqe->task_stat.write_stat.num_r2ts; conn->txdata_octets += bnx2i_cmd->req.total_data_transfer_length; + ADD_STATS_64(hba, tx_pdus, + resp_cqe->task_stat.write_stat.num_data_outs); + ADD_STATS_64(hba, tx_bytes, + bnx2i_cmd->req.total_data_transfer_length); + ADD_STATS_64(hba, rx_pdus, + resp_cqe->task_stat.write_stat.num_r2ts); } bnx2i_iscsi_unmap_sg_list(bnx2i_cmd); @@ -1426,6 +1438,45 @@ fail: } +static void bnx2i_login_stats(struct bnx2i_hba *hba, u8 status_class, + u8 status_detail) +{ + switch (status_class) { + case ISCSI_STATUS_CLS_SUCCESS: + hba->login_stats.successful_logins++; + break; + case ISCSI_STATUS_CLS_REDIRECT: + switch (status_detail) { + case ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP: + case ISCSI_LOGIN_STATUS_TGT_MOVED_PERM: + hba->login_stats.login_redirect_responses++; + break; + default: + hba->login_stats.login_failures++; + break; + } + break; + case ISCSI_STATUS_CLS_INITIATOR_ERR: + hba->login_stats.login_failures++; + switch (status_detail) { + case ISCSI_LOGIN_STATUS_AUTH_FAILED: + case ISCSI_LOGIN_STATUS_TGT_FORBIDDEN: + hba->login_stats.login_authentication_failures++; + break; + default: + /* Not sure, but treating all other class2 errors as + login negotiation failure */ + hba->login_stats.login_negotiation_failures++; + break; + } + break; + default: + hba->login_stats.login_failures++; + break; + } +} + + /** * bnx2i_process_login_resp - this function handles iscsi login response * @session: iscsi session pointer @@ -1470,6 +1521,10 @@ static int bnx2i_process_login_resp(struct iscsi_session *session, resp_hdr->status_class = login->status_class; resp_hdr->status_detail = login->status_detail; pld_len = login->data_length; + + bnx2i_login_stats(bnx2i_conn->hba, login->status_class, + login->status_detail); + bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf + pld_len; @@ -1633,7 +1688,6 @@ static int bnx2i_process_logout_resp(struct iscsi_session *session, resp_hdr->t2retain = cpu_to_be32(logout->time_to_retain); __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0); - bnx2i_conn->ep->state = EP_STATE_LOGOUT_RESP_RCVD; done: spin_unlock(&session->lock); @@ -1660,8 +1714,11 @@ static void bnx2i_process_nopin_local_cmpl(struct iscsi_session *session, spin_lock(&session->lock); task = iscsi_itt_to_task(conn, nop_in->itt & ISCSI_NOP_IN_MSG_INDEX); - if (task) - __iscsi_put_task(task); + if (task) { + spin_unlock(&session->lock); + iscsi_put_task(task); + spin_lock(&session->lock); + } spin_unlock(&session->lock); } @@ -1723,7 +1780,7 @@ static int bnx2i_process_nopin_mesg(struct iscsi_session *session, hdr->flags = ISCSI_FLAG_CMD_FINAL; hdr->itt = task->hdr->itt; hdr->ttt = cpu_to_be32(nop_in->ttt); - memcpy(&hdr->lun, nop_in->lun, 8); + memcpy(&hdr->lun, nop_in->lun, sizeof(struct scsi_lun)); } done: __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); @@ -1766,7 +1823,7 @@ static void bnx2i_process_async_mesg(struct iscsi_session *session, resp_hdr->opcode = async_cqe->op_code; resp_hdr->flags = 0x80; - memcpy(&resp_hdr->lun, async_cqe->lun, 8); + memcpy(&resp_hdr->lun, async_cqe->lun, sizeof(struct scsi_lun)); resp_hdr->exp_cmdsn = cpu_to_be32(async_cqe->exp_cmd_sn); resp_hdr->max_cmdsn = cpu_to_be32(async_cqe->max_cmd_sn); @@ -1860,6 +1917,7 @@ int bnx2i_percpu_io_thread(void *arg) LIST_HEAD(work_list); set_user_nice(current, -20); + set_unfreezable(current); while (!kthread_should_stop()) { spin_lock_bh(&p->p_work_lock); @@ -1905,10 +1963,11 @@ static int bnx2i_queue_scsi_cmd_resp(struct iscsi_session *session, { struct bnx2i_work *bnx2i_work = NULL; struct bnx2i_percpu_s *p = NULL; + struct bnx2i_cmd *bnx2i_cmd; + //struct scsi_cmnd *sc; struct iscsi_task *task; - struct scsi_cmnd *sc; - int rc = 0; int cpu; + int rc = 0; spin_lock(&session->lock); task = iscsi_itt_to_task(bnx2i_conn->cls_conn->dd_data, @@ -1917,12 +1976,16 @@ static int bnx2i_queue_scsi_cmd_resp(struct iscsi_session *session, spin_unlock(&session->lock); return -EINVAL; } + bnx2i_cmd = task->dd_data; + cpu = bnx2i_cmd->cpu; + /* Avoid using sc->request->cpu for now as the blk layer has a bug sc = task->sc; - - if (!blk_rq_cpu_valid(sc->request)) - cpu = smp_processor_id(); - else + if (!blk_rq_cpu_valid(sc->request)) { + cpu = get_cpu(); + put_cpu(); + } else cpu = sc->request->cpu; + */ spin_unlock(&session->lock); @@ -1964,6 +2027,7 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) { struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; struct iscsi_session *session = conn->session; + struct bnx2i_hba *hba = bnx2i_conn->hba; struct qp_info *qp; struct bnx2i_nop_in_msg *nopin; int tgt_async_msg; @@ -1976,7 +2040,7 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) if (!qp->cq_virt) { printk(KERN_ALERT "bnx2i (%s): cq resr freed in bh execution!", - bnx2i_conn->hba->netdev->name); + hba->netdev->name); goto out; } while (1) { @@ -1990,7 +2054,7 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) printk(KERN_ALERT "bnx2i: Unsolicited " "NOP-In detected for suspended " "connection dev=%s!\n", - bnx2i_conn->hba->netdev->name); + hba->netdev->name); bnx2i_unsol_pdu_adjust_rq(bnx2i_conn); goto cqe_out; } @@ -1998,13 +2062,15 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) } tgt_async_msg = 0; + /* Rx PDU/data length count */ + switch (nopin->op_code) { case ISCSI_OP_SCSI_CMD_RSP: case ISCSI_OP_SCSI_DATA_IN: /* Run the kthread engine only for data cmds All other cmds will be completed in this bh! */ bnx2i_queue_scsi_cmd_resp(session, bnx2i_conn, nopin); - break; + goto done; case ISCSI_OP_LOGIN_RSP: bnx2i_process_login_resp(session, bnx2i_conn, qp->cq_cons_qe); @@ -2047,6 +2113,10 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n", nopin->op_code); } + + ADD_STATS_64(hba, rx_pdus, 1); + ADD_STATS_64(hba, rx_bytes, nopin->data_length); +done: if (!tgt_async_msg) { if (!atomic_read(&bnx2i_conn->ep->num_active_cmds)) printk(KERN_ALERT "bnx2i (%s): no active cmd! " @@ -2158,8 +2228,26 @@ static void bnx2i_process_update_conn_cmpl(struct bnx2i_hba *hba, static void bnx2i_recovery_que_add_conn(struct bnx2i_hba *hba, struct bnx2i_conn *bnx2i_conn) { - iscsi_conn_failure(bnx2i_conn->cls_conn->dd_data, - ISCSI_ERR_CONN_FAILED); + int prod_idx = hba->conn_recov_prod_idx; + struct iscsi_conn *conn; + + if (!hba) + return; + + hba->login_stats.session_failures++; + spin_lock(&hba->lock); + + conn = bnx2i_conn->cls_conn->dd_data; + + hba->conn_recov_list[prod_idx] = conn; + if (hba->conn_recov_max_idx == hba->conn_recov_prod_idx) + hba->conn_recov_prod_idx = 0; + else + hba->conn_recov_prod_idx++; + + spin_unlock(&hba->lock); + + schedule_work(&hba->err_rec_task); } @@ -2236,9 +2324,11 @@ static void bnx2i_process_iscsi_error(struct bnx2i_hba *hba, switch (iscsi_err->completion_status) { case ISCSI_KCQE_COMPLETION_STATUS_HDR_DIG_ERR: strcpy(additional_notice, "hdr digest err"); + hba->login_stats.digest_errors++; break; case ISCSI_KCQE_COMPLETION_STATUS_DATA_DIG_ERR: strcpy(additional_notice, "data digest err"); + hba->login_stats.digest_errors++; break; case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_OPCODE: strcpy(additional_notice, "wrong opcode rcvd"); @@ -2529,7 +2619,7 @@ static void bnx2i_indicate_kcqe(void *context, struct kcqe *kcqe[], * bnx2i_indicate_netevent - Generic netdev event handler * @context: adapter structure pointer * @event: event type - * @vlan_id: vlans id - associated vlan id with this event + * @vlan_id: vlan id - associated vlan id with this event * * Handles four netdev events, NETDEV_UP, NETDEV_DOWN, * NETDEV_GOING_DOWN and NETDEV_CHANGE @@ -2538,24 +2628,73 @@ static void bnx2i_indicate_netevent(void *context, unsigned long event, u16 vlan_id) { struct bnx2i_hba *hba = context; + struct bnx2i_conn *bnx2i_conn; + struct iscsi_conn *conn; + struct iscsi_session *session; + int conns_active, i; + unsigned long flags; /* Ignore all netevent coming from vlans */ - if (vlan_id != 0) + if (vlan_id) return; switch (event) { case NETDEV_UP: - if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state)) - bnx2i_send_fw_iscsi_init_msg(hba); break; case NETDEV_DOWN: + for (i = 0; i < hba->max_active_conns; i++) { + bnx2i_conn = bnx2i_get_conn_from_id(hba, i); + if (bnx2i_conn) { + conn = bnx2i_conn->cls_conn->dd_data; + session = conn->session; + spin_lock_irqsave(&session->lock, flags); + if (session->state == ISCSI_STATE_FAILED) + session->state = + bnx2i_conn->prev_sess_state; + spin_unlock_irqrestore(&session->lock, flags); + } + } + iscsi_host_for_each_session(hba->shost, + bnx2i_drop_session); + while (hba->ofld_conns_active) { + conns_active = hba->ofld_conns_active; + + wait_event_interruptible_timeout(hba->eh_wait, + (hba->ofld_conns_active != conns_active), + hba->hba_shutdown_tmo); + if (hba->ofld_conns_active == conns_active) + break; + } + /* This flag should be cleared last so that ep_disconnect() + * gracefully cleans up connection context + */ clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state); clear_bit(ADAPTER_STATE_UP, &hba->adapter_state); + if (hba->ofld_conns_active) + printk(KERN_ERR "bnx2i (%s): NETDEV_DOWN with %d " + "active conns\n", hba->netdev->name, + hba->ofld_conns_active); break; + case NETDEV_GOING_DOWN: set_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state); - iscsi_host_for_each_session(hba->shost, - bnx2i_drop_session); + /* Suspend all data transmissions */ + for (i = 0; i < hba->max_active_conns; i++) { + bnx2i_conn = bnx2i_get_conn_from_id(hba, i); + if (bnx2i_conn) { + conn = bnx2i_conn->cls_conn->dd_data; + session = conn->session; + spin_lock_irqsave(&session->lock, flags); + bnx2i_conn->prev_sess_state = session->state; + if (conn->stop_stage == 0) + session->state = ISCSI_STATE_FAILED; + spin_unlock_irqrestore(&session->lock, flags); + iscsi_suspend_queue(conn); + set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); + iscsi_conn_error_event(bnx2i_conn->cls_conn, + ISCSI_ERR_CONN_FAILED); + } + } break; case NETDEV_CHANGE: bnx2i_get_link_state(hba); @@ -2575,16 +2714,19 @@ static void bnx2i_indicate_netevent(void *context, unsigned long event, */ static void bnx2i_cm_connect_cmpl(struct cnic_sock *cm_sk) { - struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context; + struct bnx2i_endpoint *bnx2i_ep = + (struct bnx2i_endpoint *) cm_sk->context; - if (test_bit(ADAPTER_STATE_GOING_DOWN, &ep->hba->adapter_state)) - ep->state = EP_STATE_CONNECT_FAILED; + if (test_bit(ADAPTER_STATE_GOING_DOWN, &bnx2i_ep->hba->adapter_state)) + bnx2i_ep->state = EP_STATE_CONNECT_FAILED; else if (test_bit(SK_F_OFFLD_COMPLETE, &cm_sk->flags)) - ep->state = EP_STATE_CONNECT_COMPL; - else - ep->state = EP_STATE_CONNECT_FAILED; - - wake_up_interruptible(&ep->ofld_wait); + bnx2i_ep->state = EP_STATE_CONNECT_COMPL; + else { + /* For option2 connection, treat all failures as timeouts */ + bnx2i_ep->hba->login_stats.connection_timeouts++; + bnx2i_ep->state = EP_STATE_CONNECT_FAILED; + } + wake_up_interruptible(&bnx2i_ep->ofld_wait); } @@ -2660,7 +2802,7 @@ static void bnx2i_cm_remote_abort(struct cnic_sock *cm_sk) static int bnx2i_send_nl_mesg(void *context, u32 msg_type, - char *buf, u16 buflen) + char *buf, u16 buflen, u32 iface_num) { struct bnx2i_hba *hba = context; int rc; @@ -2668,8 +2810,8 @@ static int bnx2i_send_nl_mesg(void *context, u32 msg_type, if (!hba) return -ENODEV; - rc = iscsi_offload_mesg(hba->shost, &bnx2i_iscsi_transport, - msg_type, buf, buflen); + rc = bnx2i_offload_mesg(hba->shost, &bnx2i_iscsi_transport, + msg_type, buf, buflen, iface_num); if (rc) printk(KERN_ALERT "bnx2i: private nl message send error\n"); @@ -2683,6 +2825,7 @@ static int bnx2i_send_nl_mesg(void *context, u32 msg_type, * */ struct cnic_ulp_ops bnx2i_cnic_cb = { + .version = CNIC_ULP_OPS_VER, .cnic_init = bnx2i_ulp_init, .cnic_exit = bnx2i_ulp_exit, .cnic_start = bnx2i_start, @@ -2695,6 +2838,7 @@ struct cnic_ulp_ops bnx2i_cnic_cb = { .cm_remote_close = bnx2i_cm_remote_close, .cm_remote_abort = bnx2i_cm_remote_abort, .iscsi_nl_send_msg = bnx2i_send_nl_mesg, + .cnic_get_stats = bnx2i_get_stats, .owner = THIS_MODULE }; @@ -2744,10 +2888,10 @@ int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep) ep->qp.ctx_base = ioremap_nocache(ep->hba->reg_base + reg_off, MB_KERNEL_CTX_SIZE); +arm_cq: if (!ep->qp.ctx_base) return -ENOMEM; -arm_cq: bnx2i_arm_cq_event_coalescing(ep, CNIC_ARM_CQE); return 0; } diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c index 8b6816706ee5..e3622d811bf8 100644 --- a/drivers/scsi/bnx2i/bnx2i_init.c +++ b/drivers/scsi/bnx2i/bnx2i_init.c @@ -18,17 +18,15 @@ static struct list_head adapter_list = LIST_HEAD_INIT(adapter_list); static u32 adapter_count; #define DRV_MODULE_NAME "bnx2i" -#define DRV_MODULE_VERSION "2.7.2.2" -#define DRV_MODULE_RELDATE "Apr 25, 2012" +#define DRV_MODULE_VERSION "2.7.4.1f" +#define DRV_MODULE_RELDATE "Aug 27, 2012" static char version[] __devinitdata = "Broadcom NetXtreme II iSCSI Driver " DRV_MODULE_NAME \ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; - MODULE_AUTHOR("Anil Veerabhadrappa and " "Eddie Wai "); - MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/57710/57711/57712" "/57800/57810/57840 iSCSI Driver"); MODULE_LICENSE("GPL"); @@ -48,6 +46,10 @@ unsigned int en_tcp_dack = 1; module_param(en_tcp_dack, int, 0664); MODULE_PARM_DESC(en_tcp_dack, "Enable TCP Delayed ACK"); +unsigned int time_stamps = 0x00; +module_param(time_stamps, int, 0664); +MODULE_PARM_DESC(time_stamps, "Enable TCP TimeStamps"); + unsigned int error_mask1 = 0x00; module_param(error_mask1, uint, 0664); MODULE_PARM_DESC(error_mask1, "Config FW iSCSI Error Mask #1"); @@ -64,6 +66,14 @@ unsigned int rq_size = BNX2I_RQ_WQES_DEFAULT; module_param(rq_size, int, 0664); MODULE_PARM_DESC(rq_size, "Configure RQ size"); +unsigned int tcp_buf_size = 64; +module_param(tcp_buf_size, int, 0664); +MODULE_PARM_DESC(tcp_buf_size, "TCP send/receive buffer size"); + +unsigned int last_active_tcp_port = 0x00; +module_param(last_active_tcp_port, int, 0664); +MODULE_PARM_DESC(last_active_tcp_port, "Display last tcp src port info"); + u64 iscsi_error_mask = 0x00; DEFINE_PER_CPU(struct bnx2i_percpu_s, bnx2i_percpu); @@ -101,7 +111,8 @@ void bnx2i_identify_device(struct bnx2i_hba *hba) hba->pci_did == PCI_DEVICE_ID_NX2_57711 || hba->pci_did == PCI_DEVICE_ID_NX2_57711E || hba->pci_did == PCI_DEVICE_ID_NX2_57712 || - hba->pci_did == PCI_DEVICE_ID_NX2_57712E || + hba->pci_did == PCI_DEVICE_ID_NX2_57712_VF || + hba->pci_did == PCI_DEVICE_ID_NX2_57712_MF || hba->pci_did == PCI_DEVICE_ID_NX2_57800 || hba->pci_did == PCI_DEVICE_ID_NX2_57800_MF || hba->pci_did == PCI_DEVICE_ID_NX2_57800_VF || @@ -316,6 +327,7 @@ static int bnx2i_init_one(struct bnx2i_hba *hba, struct cnic_dev *cnic) else printk(KERN_ERR "bnx2i dev reg, unknown error, %d\n", rc); + set_bit(CNIC_F_ISCSI_OOO_ENABLE, &cnic->flags); out: mutex_unlock(&bnx2i_dev_lock); @@ -335,17 +347,26 @@ void bnx2i_ulp_init(struct cnic_dev *dev) { struct bnx2i_hba *hba; + if (dev->version != CNIC_DEV_VER) { + printk(KERN_WARNING "bnx2i init: cnic not compatible, " + "expecting: 0x%x got: 0x%x\n", + CNIC_DEV_VER, dev->version); + return; + } + /* Allocate a HBA structure for this device */ hba = bnx2i_alloc_hba(dev); if (!hba) { - printk(KERN_ERR "bnx2i init: hba initialization failed\n"); + printk(KERN_ERR "bnx2i init: (%s) hba initialization failed\n", + dev->netdev->name); return; } /* Get PCI related information and update hba struct members */ clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic); if (bnx2i_init_one(hba, dev)) { - printk(KERN_ERR "bnx2i - hba %p init failed\n", hba); + printk(KERN_ERR "bnx2i init: (%s) hba %p init failed\n", + dev->netdev->name, hba); bnx2i_free_hba(hba); } } @@ -380,6 +401,47 @@ void bnx2i_ulp_exit(struct cnic_dev *dev) } +/** + * bnx2i_get_stats - Retrieve various statistic from iSCSI offload + * @handle: bnx2i_hba + * + * function callback exported via bnx2i - cnic driver interface to + * retrieve various iSCSI offload related statistics. + */ +int bnx2i_get_stats(void *handle) +{ + struct bnx2i_hba *hba = handle; + struct iscsi_stats_info *stats; + + if (!hba) + return -EINVAL; + + stats = (struct iscsi_stats_info *)hba->cnic->stats_addr; + + if (!stats) + return -ENOMEM; + + memcpy(stats->version, DRV_MODULE_VERSION, sizeof(stats->version)); + memcpy(stats->mac_add1 + 2, hba->cnic->mac_addr, ETH_ALEN); + + stats->max_frame_size = hba->netdev->mtu; + stats->txq_size = hba->max_sqes; + stats->rxq_size = hba->max_cqes; + + /* Loop through all ep to get the cqe_left average */ + stats->txq_avg_depth = 0; + stats->rxq_avg_depth = 0; + + GET_STATS_64(hba, stats, rx_pdus); + GET_STATS_64(hba, stats, rx_bytes); + + GET_STATS_64(hba, stats, tx_pdus); + GET_STATS_64(hba, stats, tx_bytes); + + return 0; +} + + /** * bnx2i_percpu_thread_create - Create a receive thread for an * online CPU @@ -494,7 +556,7 @@ static int __init bnx2i_mod_init(void) goto out; } - err = cnic_register_driver(CNIC_ULP_ISCSI, &bnx2i_cnic_cb); + err = cnic_register_driver2(CNIC_ULP_ISCSI, &bnx2i_cnic_cb); if (err) { printk(KERN_ERR "Could not register bnx2i cnic driver.\n"); goto unreg_xport; @@ -558,7 +620,7 @@ static void __exit bnx2i_mod_exit(void) bnx2i_percpu_thread_destroy(cpu); iscsi_unregister_transport(&bnx2i_iscsi_transport); - cnic_unregister_driver(CNIC_ULP_ISCSI); + cnic_unregister_driver2(CNIC_ULP_ISCSI); } module_init(bnx2i_mod_init); diff --git a/drivers/scsi/bnx2i/bnx2i_ioctl.h b/drivers/scsi/bnx2i/bnx2i_ioctl.h new file mode 100644 index 000000000000..66ffdadb531b --- /dev/null +++ b/drivers/scsi/bnx2i/bnx2i_ioctl.h @@ -0,0 +1,47 @@ +/* bnx2i_ioctl.h: Broadcom NetXtreme II iSCSI driver. + * + * Copyright (c) 2006 - 2012 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: Anil Veerabhadrappa (anilgv@broadcom.com) + * Maintained by: Eddie Wai (eddie.wai@broadcom.com) + */ +#ifndef _BNX2I_IOCTL_H +#define _BNX2I_IOCTL_H + +#define MAX_SIG_SIZE 32 +#define MAX_XPORT_NAME 16 +#define MAX_DEV_NAME_SIZE 16 + +#define BNX2I_MGMT_SIGNATURE "bnx2i-mgmt:1.0" + + + +struct bnx2i_ioctl_header { + char signature[MAX_SIG_SIZE]; + char xport_name[MAX_XPORT_NAME]; + char dev_name[MAX_DEV_NAME_SIZE]; +}; + + +struct bnx2i_get_port_count { + struct bnx2i_ioctl_header hdr; + unsigned int port_count; +}; + +struct bnx2i_set_port_num { + struct bnx2i_ioctl_header hdr; + unsigned int num_ports; + unsigned short tcp_port[1]; +}; + + +#define BNX2I_IOCTL_GET_PORT_REQ \ + _IOWR('I', 101, struct bnx2i_get_port_count) +#define BNX2I_IOCTL_SET_TCP_PORT \ + _IOWR('I', 102, struct bnx2i_set_port_num) + +#endif diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index e5fd7ab7cb3a..f33c2b63ceb0 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -13,15 +13,25 @@ * Maintained by: Eddie Wai (eddie.wai@broadcom.com) */ -#include -#include -#include #include "bnx2i.h" +#include +#include +#include + +#include struct scsi_transport_template *bnx2i_scsi_xport_template; struct iscsi_transport bnx2i_iscsi_transport; static struct scsi_host_template bnx2i_host_template; +#if defined(INIT_DELAYED_WORK_DEFERRABLE) || defined(INIT_WORK_NAR) +static void conn_err_recovery_task(struct work_struct *work); +#else +static void conn_err_recovery_task(void *data); +#endif + +static void bnx2i_withdraw_conn_recovery(struct bnx2i_hba *hba, + struct iscsi_conn *conn); /* * Global endpoint resource info */ @@ -372,9 +382,6 @@ static void bnx2i_release_free_cid_que(struct bnx2i_hba *hba) * bnx2i_alloc_ep - allocates ep structure from global pool * @hba: pointer to adapter instance * - * routine allocates a free endpoint structure from global pool and - * a tcp port to be used for this connection. Global resource lock, - * 'bnx2i_resc_lock' is held while accessing shared global data structures */ static struct iscsi_endpoint *bnx2i_alloc_ep(struct bnx2i_hba *hba) { @@ -593,6 +600,22 @@ void bnx2i_drop_session(struct iscsi_cls_session *cls_session) iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); } +/** + * bnx2i_invalid_host - notifies iscsid of host invalidation (prep for host + * removal) + * @hba: adapter instance pointer + * @session: iscsi session pointer + * + * This notifies iscsid to invalidate the host + * + * This relies on caller using the iscsi class iterator so the object + * is refcounted and does not disapper from under us. + */ +void bnx2i_invalid_host(struct iscsi_cls_session *cls_session) +{ + iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_INVALID_HOST); +} + /** * bnx2i_ep_destroy_list_add - add an entry to EP destroy list * @hba: pointer to adapter instance @@ -874,6 +897,28 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic) hba->conn_ctx_destroy_tmo = 2 * HZ; } +#if defined(INIT_DELAYED_WORK_DEFERRABLE) || defined(INIT_WORK_NAR) + INIT_WORK(&hba->err_rec_task, conn_err_recovery_task); +#else + INIT_WORK(&hba->err_rec_task, conn_err_recovery_task, hba); +#endif + hba->conn_recov_prod_idx = 0; + hba->conn_recov_cons_idx = 0; + hba->conn_recov_max_idx = 0; + hba->conn_recov_list = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!hba->conn_recov_list) + goto free_dump_mem; + hba->conn_recov_max_idx = PAGE_SIZE / sizeof (struct iscsi_conn *) - 1; + +#ifdef CONFIG_32BIT + spin_lock_init(&hba->stat_lock); +#endif + memset(&hba->stats, 0, sizeof(struct iscsi_stats_info)); + memset(&hba->login_stats, 0, sizeof(struct iscsi_login_stats_info)); + + INIT_LIST_HEAD(&hba->iface_ipv4_list); + INIT_LIST_HEAD(&hba->iface_ipv6_list); + if (iscsi_host_add(shost, &hba->pcidev->dev)) goto free_dump_mem; return hba; @@ -902,8 +947,42 @@ ioreg_map_err: void bnx2i_free_hba(struct bnx2i_hba *hba) { struct Scsi_Host *shost = hba->shost; - + struct iscsi_host *ihost = shost_priv(shost); +#define MAX_FREE_HBA_RETRY 10 + int i = MAX_FREE_HBA_RETRY, rc = 0; +#if ((defined(__SLES_DISTRO__) && (__SLES_DISTRO__ > 0x1102)) || \ + (defined(__RHELS_DISTRO_5__) && (__RHELS_DISTRO_5__ > 0x0508)) || \ + (defined(__RHELS_DISTRO_6__) && (__RHELS_DISTRO_6__ > 0x0602)) || \ + (!defined(__DISTRO__) && LINUX_VERSION_CODE > 0x020628)) + struct list_head *pos, *q; + struct bnx2i_iface *bnx2i_iface; +#endif + + /* Before calling host_remove, we must ensure that all sessions + * are currently down via the ihost->num_session parameter + */ + while (i--) { + iscsi_host_for_each_session(hba->shost, + bnx2i_invalid_host); + /* 10s timeout */ + rmb(); + rc = wait_event_interruptible_timeout(hba->eh_wait, + ihost->num_sessions == 0, + msecs_to_jiffies(10000)); + rmb(); + if (ihost->num_sessions) { + printk(KERN_ALERT "bnx2i: %s free_hba retry with " + "num_sessions = %d\n", hba->netdev->name, + ihost->num_sessions); + } else { + printk(KERN_ALERT "bnx2i: %s free_hba done after %d " + "retries\n", hba->netdev->name, + (MAX_FREE_HBA_RETRY - 1) - i); + break; + } + } iscsi_host_remove(shost); + INIT_LIST_HEAD(&hba->ep_ofld_list); INIT_LIST_HEAD(&hba->ep_active_list); INIT_LIST_HEAD(&hba->ep_destroy_list); @@ -915,6 +994,37 @@ void bnx2i_free_hba(struct bnx2i_hba *hba) } bnx2i_free_mp_bdt(hba); bnx2i_release_free_cid_que(hba); + + if (hba->conn_recov_list) { + kfree(hba->conn_recov_list); + hba->conn_recov_list = NULL; + } + +#if ((defined(__SLES_DISTRO__) && (__SLES_DISTRO__ > 0x1102)) || \ + (defined(__RHELS_DISTRO_5__) && (__RHELS_DISTRO_5__ > 0x0508)) || \ + (defined(__RHELS_DISTRO_6__) && (__RHELS_DISTRO_6__ > 0x0602)) || \ + (!defined(__DISTRO__) && LINUX_VERSION_CODE > 0x020628)) + list_for_each_safe(pos, q, &hba->iface_ipv4_list) { + bnx2i_iface = list_entry(pos, struct bnx2i_iface, link); + if (bnx2i_iface) { + if (bnx2i_iface->iface) + iscsi_destroy_iface(bnx2i_iface->iface); + list_del(pos); + kfree(bnx2i_iface); + } + } + INIT_LIST_HEAD(&hba->iface_ipv4_list); + list_for_each_safe(pos, q, &hba->iface_ipv6_list) { + bnx2i_iface = list_entry(pos, struct bnx2i_iface, link); + if (bnx2i_iface) { + if (bnx2i_iface->iface) + iscsi_destroy_iface(bnx2i_iface->iface); + list_del(pos); + kfree(bnx2i_iface); + } + } + INIT_LIST_HEAD(&hba->iface_ipv6_list); +#endif iscsi_host_free(shost); } @@ -1078,6 +1188,22 @@ static int bnx2i_iscsi_send_generic_request(struct iscsi_task *task) char *buf; int data_len; + /* + * Forcefully terminate all in progress connection recovery at the + * earliest, either in bind(), send_pdu(LOGIN), or conn_start() + */ + if (bnx2i_adapter_ready(bnx2i_conn->ep->hba)) { + if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == + ISCSI_OP_NOOP_OUT) + /* This is a WA to indicate to libiscsi that the nopout + * request was sent successfully without actually + * submitting to the hardware. + * Just silently drop the nopout request + */ + return 0; + else + return -EIO; + } bnx2i_iscsi_prep_generic_pdu_bd(bnx2i_conn); switch (task->hdr->opcode & ISCSI_OPCODE_MASK) { case ISCSI_OP_LOGIN: @@ -1181,12 +1307,18 @@ static int bnx2i_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task) { struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; struct bnx2i_cmd *cmd = task->dd_data; memset(bnx2i_conn->gen_pdu.req_buf, 0, ISCSI_DEF_MAX_RECV_SEG_LEN); bnx2i_setup_cmd_wqe_template(cmd); bnx2i_conn->gen_pdu.req_buf_size = task->data_count; + + /* Tx PDU/data length count */ + ADD_STATS_64(hba, tx_pdus, 1); + ADD_STATS_64(hba, tx_bytes, task->data_count); + if (task->data_count) { memcpy(bnx2i_conn->gen_pdu.req_buf, task->data, task->data_count); @@ -1195,6 +1327,7 @@ bnx2i_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task) } cmd->conn = conn->dd_data; cmd->scsi_cmd = NULL; + return bnx2i_iscsi_send_generic_request(task); } @@ -1214,17 +1347,28 @@ static int bnx2i_task_xmit(struct iscsi_task *task) struct scsi_cmnd *sc = task->sc; struct bnx2i_cmd *cmd = task->dd_data; struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr; + u32 cpu; + /* If the number of outstanding cmds exceeds max_sqes, bail */ if (atomic_read(&bnx2i_conn->ep->num_active_cmds) + 1 > - hba->max_sqes) + hba->max_sqes) { + printk(KERN_ERR "bnx2i: cmds full=%d max=%d\n", + atomic_read(&bnx2i_conn->ep->num_active_cmds), + hba->max_sqes); return -ENOMEM; - + } /* * If there is no scsi_cmnd this must be a mgmt task */ if (!sc) return bnx2i_mtask_xmit(conn, task); + /* Keep track of the CPU number for the respective scsi cmds */ + /* Avoid sc->request->cpu for now as the blk layer code has a bug */ + cpu = get_cpu(); + put_cpu(); + cmd->cpu = cpu; + bnx2i_setup_cmd_wqe_template(cmd); cmd->req.op_code = ISCSI_OP_SCSI_CMD; cmd->conn = bnx2i_conn; @@ -1430,6 +1574,13 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session, hba->netdev->name); return -EEXIST; } + if (bnx2i_conn->ep) { + printk(KERN_ALERT "bnx2i: Binding to an existing endpoint " + "detected. Disconnecting the old...\n"); + mutex_lock(&hba->net_dev_lock); + bnx2i_hw_ep_disconnect(bnx2i_conn->ep); + mutex_unlock(&hba->net_dev_lock); + } bnx2i_ep->conn = bnx2i_conn; bnx2i_conn->ep = bnx2i_ep; bnx2i_conn->iscsi_conn_cid = bnx2i_ep->ep_iscsi_cid; @@ -1471,6 +1622,8 @@ static void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn) bnx2i_conn_free_login_resources(hba, bnx2i_conn); + bnx2i_withdraw_conn_recovery(hba, conn); + if (atomic_read(&bnx2i_conn->work_cnt)) { for_each_online_cpu(cpu) { p = &per_cpu(bnx2i_percpu, cpu); @@ -1493,6 +1646,54 @@ static void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn) iscsi_conn_teardown(cls_conn); } +#if ((!defined(__SLES_DISTRO__) || (__SLES_DISTRO__ < 0x1102)) && \ + (!defined(__RHELS_DISTRO_5__) || (__RHELS_DISTRO_5__ < 0x0509)) && \ + (!defined(__RHELS_DISTRO_6__) || (__RHELS_DISTRO_6__ < 0x0602)) && \ + (defined(__DISTRO__) || LINUX_VERSION_CODE < 0x020626)) +/** + * bnx2i_conn_get_param - return iscsi connection parameter to caller + * @cls_conn: pointer to iscsi cls conn + * @param: parameter type identifier + * @buf: buffer pointer + * + * returns iSCSI connection parameters + */ +static int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct bnx2i_hba *hba; + int len = 0; + + switch (param) { + case ISCSI_PARAM_CONN_PORT: + case ISCSI_PARAM_CONN_ADDRESS: + /* iscsi_conn is protected by the caller */ + if (!(bnx2i_conn && bnx2i_conn->hba)) + break; + hba = bnx2i_conn->hba; + /* lock ep to conn/cm_sk */ + mutex_lock(&hba->net_dev_lock); + if (!(bnx2i_conn->ep && bnx2i_conn->ep->cm_sk)) + goto out; + + if (param == ISCSI_PARAM_CONN_PORT) + len = sprintf(buf, "%hu\n", + bnx2i_conn->ep->cm_sk->dst_port); + else + len = sprintf(buf, "%pI4\n", + &bnx2i_conn->ep->cm_sk->dst_ip); +out: + mutex_unlock(&hba->net_dev_lock); + break; + default: + len = iscsi_conn_get_param(cls_conn, param, buf); + } + return len; +} + +#else /** * bnx2i_ep_get_param - return iscsi ep parameter to caller @@ -1528,9 +1729,155 @@ static int bnx2i_ep_get_param(struct iscsi_endpoint *ep, default: return -ENOSYS; } + return len; +} +#endif + +#if ((defined(__SLES_DISTRO__) && (__SLES_DISTRO__ > 0x1102)) || \ + (defined(__RHELS_DISTRO_5__) && (__RHELS_DISTRO_5__ > 0x0508)) || \ + (defined(__RHELS_DISTRO_6__) && (__RHELS_DISTRO_6__ > 0x0602)) || \ + (!defined(__DISTRO__) && LINUX_VERSION_CODE > 0x020628)) +/** + * bnx2i_set_iface_param - sets iface related parameters + * @shost: scsi host pointer + * @data: iface data string + * @len: length + */ +static int bnx2i_set_iface_param(struct Scsi_Host *shost, + void *data, u32 len) +{ + struct bnx2i_hba *hba = iscsi_host_priv(shost); + struct iscsi_iface_param_info *iface_param = NULL; + struct nlattr *attr; + u32 rem = len; + struct bnx2i_iface *bnx2i_iface; + struct iscsi_iface *iface; + struct list_head *pos, *q, *ip_list; + if (!hba) + return 0; + + nla_for_each_attr(attr, data, len, rem) { + iface_param = nla_data(attr); + + if (iface_param->param_type != ISCSI_NET_PARAM) + continue; + + /* Ignore all other param request */ + if (iface_param->param != ISCSI_NET_PARAM_IFACE_ENABLE) + continue; + + switch (iface_param->iface_type) { + case ISCSI_IFACE_TYPE_IPV4: + ip_list = &hba->iface_ipv4_list; + break; + case ISCSI_IFACE_TYPE_IPV6: + ip_list = &hba->iface_ipv6_list; + break; + default: + printk(KERN_ERR "bnx2i: Invalid iface type\n"); + continue; + } + list_for_each_safe(pos, q, ip_list) { + bnx2i_iface = list_entry(pos, + struct bnx2i_iface, + link); + if (bnx2i_iface->iface->iface_num == + iface_param->iface_num) { + /* iface found, delete */ + if (bnx2i_iface->iface) { + iscsi_destroy_iface(bnx2i_iface->iface); + list_del(pos); + kfree(bnx2i_iface); + } + break; + } + } + if (iface_param->value[0] != ISCSI_IFACE_ENABLE) + goto done; + + /* Allocate for the new bnx2i iface entry */ + bnx2i_iface = kmalloc(sizeof(struct bnx2i_iface), GFP_ATOMIC); + if (!bnx2i_iface) { + printk(KERN_ERR "bnx2i: bnx2i_iface alloc failed\n"); + return -ENOMEM; + } + iface = iscsi_create_iface(shost, + &bnx2i_iscsi_transport, + iface_param->iface_type, + iface_param->iface_num, 0); + if (!iface) { + printk(KERN_ERR "bnx2i: iscsi_iface create failed\n"); + kfree(bnx2i_iface); + return -ENOMEM; + } + bnx2i_iface->iface = iface; + list_add_tail(&bnx2i_iface->link, ip_list); + } +done: + return 0; +} + + +/** +* bnx2i_get_iface_param - gets iface related parameters +* @shost: scsi host pointer +* @data: iface data string +* @len: length +*/ +static int bnx2i_get_iface_param(struct iscsi_iface *iface, + enum iscsi_param_type param_type, + int param, char *buf) +{ + struct Scsi_Host *shost = iscsi_iface_to_shost(iface); + struct bnx2i_hba *hba = iscsi_host_priv(shost); + struct list_head *active_list; + int len = 0; + + if (param_type != ISCSI_NET_PARAM) + return -ENOSYS; + + if (!hba) + return -ENOSYS; + + switch (param) { + case ISCSI_NET_PARAM_IPV4_ADDR: + case ISCSI_NET_PARAM_IPV6_ADDR: + read_lock_bh(&hba->ep_rdwr_lock); + active_list = &hba->ep_active_list; + if (!list_empty(active_list)) { + struct bnx2i_endpoint *bnx2i_ep; + struct cnic_sock *csk; + + list_for_each_entry(bnx2i_ep, active_list, link) { + csk = bnx2i_ep->cm_sk; + if (!csk) + continue; + if (csk->iface_num != iface->iface_num) + continue; + /* IPv4 */ + if (!test_bit(SK_F_IPV6, &csk->flags) && + iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { + FORMAT_IP(buf, "%pI4\n", csk->src_ip, + len); + break; + } else if (test_bit(SK_F_IPV6, &csk->flags) && + iface->iface_type == ISCSI_IFACE_TYPE_IPV6) { + FORMAT_IP6(buf, "%pI6\n", csk->src_ip, + len); + break; + } + } + } + read_unlock_bh(&hba->ep_rdwr_lock); + break; + default: + break; + } return len; } +#endif + /** * bnx2i_host_get_param - returns host (adapter) related parameters @@ -1544,6 +1891,11 @@ static int bnx2i_host_get_param(struct Scsi_Host *shost, struct bnx2i_hba *hba = iscsi_host_priv(shost); int len = 0; + /* Return len = 0 if the hba or the cnic has already been + unregistered */ + if (!(hba && hba->cnic)) + return len; + switch (param) { case ISCSI_HOST_PARAM_HWADDRESS: len = sysfs_format_mac(buf, hba->cnic->mac_addr, 6); @@ -1563,10 +1915,14 @@ static int bnx2i_host_get_param(struct Scsi_Host *shost, struct bnx2i_endpoint, link); csk = bnx2i_ep->cm_sk; - if (test_bit(SK_F_IPV6, &csk->flags)) - len = sprintf(buf, "%pI6\n", csk->src_ip); - else - len = sprintf(buf, "%pI4\n", csk->src_ip); + if (csk) { + if (!test_bit(SK_F_IPV6, &csk->flags)) + FORMAT_IP(buf, "%pI4\n", csk->src_ip, + len); + else + FORMAT_IP6(buf, "%pI6\n", csk->src_ip, + len); + } } read_unlock_bh(&hba->ep_rdwr_lock); break; @@ -1607,6 +1963,9 @@ static int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn) flush_signals(current); del_timer_sync(&bnx2i_conn->ep->ofld_timer); + if (bnx2i_conn->ep->state != EP_STATE_ULP_UPDATE_COMPL) + return -EBUSY; + iscsi_conn_start(cls_conn); return 0; } @@ -1691,9 +2050,10 @@ no_nx2_route: static int bnx2i_tear_down_conn(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep) { - if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic) && ep->cm_sk) + if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic) && ep->cm_sk) { hba->cnic->cm_destroy(ep->cm_sk); - + ep->cm_sk = NULL; + } if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type) && ep->state == EP_STATE_DISCONN_TIMEDOUT) { if (ep->conn && ep->conn->cls_conn && @@ -1757,7 +2117,7 @@ static int bnx2i_tear_down_conn(struct bnx2i_hba *hba, */ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, - int non_blocking) + int non_blocking, u32 iface_num) { u32 iscsi_cid = BNX2I_CID_RESERVED; struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr; @@ -1768,6 +2128,9 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost, struct cnic_sockaddr saddr; struct iscsi_endpoint *ep; int rc = 0; +#ifndef _EP_CONNECT_IFACE_NUM_ + u32 iface_num = -1; +#endif if (shost) { /* driver is given scsi host to work with */ @@ -1860,16 +2223,21 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost, } rc = cnic->cm_create(cnic, CNIC_ULP_ISCSI, bnx2i_ep->ep_cid, - iscsi_cid, &bnx2i_ep->cm_sk, bnx2i_ep); + iscsi_cid, &bnx2i_ep->cm_sk, bnx2i_ep, iface_num); if (rc) { rc = -EINVAL; /* Need to terminate and cleanup the connection */ goto release_ep; } - bnx2i_ep->cm_sk->rcv_buf = 256 * 1024; - bnx2i_ep->cm_sk->snd_buf = 256 * 1024; - clear_bit(SK_TCP_TIMESTAMP, &bnx2i_ep->cm_sk->tcp_flags); + /* Supply Window size to be 4 bytes aligned */ + bnx2i_ep->cm_sk->rcv_buf = (tcp_buf_size * 1024 - 1) & ~0x03; + bnx2i_ep->cm_sk->snd_buf = (tcp_buf_size * 1024 - 1) & ~0x03; + + if (!en_tcp_dack) + bnx2i_ep->cm_sk->tcp_flags |= SK_TCP_NO_DELAY_ACK; + if (time_stamps) + bnx2i_ep->cm_sk->tcp_flags |= SK_TCP_TIMESTAMP; memset(&saddr, 0, sizeof(saddr)); if (dst_addr->sa_family == AF_INET) { @@ -1896,6 +2264,8 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost, if (bnx2i_map_ep_dbell_regs(bnx2i_ep)) goto del_active_ep; + + last_active_tcp_port = be16_to_cpu(bnx2i_ep->cm_sk->src_port); mutex_unlock(&hba->net_dev_lock); return ep; @@ -2122,15 +2492,16 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep) if (bnx2i_ep->conn) { bnx2i_conn = bnx2i_ep->conn; conn = bnx2i_conn->cls_conn->dd_data; + iscsi_suspend_queue(conn); } hba = bnx2i_ep->hba; mutex_lock(&hba->net_dev_lock); - if (bnx2i_ep->state == EP_STATE_DISCONN_TIMEDOUT) + if (bnx2i_ep->state == EP_STATE_DISCONN_TIMEDOUT) { goto out; - + } if (bnx2i_ep->state == EP_STATE_IDLE) goto free_resc; @@ -2226,11 +2597,102 @@ static mode_t bnx2i_attr_is_visible(int param_type, int param) default: return 0; } + case ISCSI_NET_PARAM: + switch (param) { + case ISCSI_NET_PARAM_IPV4_ADDR: + case ISCSI_NET_PARAM_IPV4_SUBNET: + case ISCSI_NET_PARAM_IPV4_GW: + case ISCSI_NET_PARAM_IPV4_BOOTPROTO: + case ISCSI_NET_PARAM_IFACE_ENABLE: + case ISCSI_NET_PARAM_IPV6_LINKLOCAL: + case ISCSI_NET_PARAM_IPV6_ADDR: + case ISCSI_NET_PARAM_IPV6_ROUTER: + case ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG: + case ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG: + case ISCSI_NET_PARAM_VLAN_ID: + case ISCSI_NET_PARAM_VLAN_PRIORITY: + case ISCSI_NET_PARAM_VLAN_ENABLED: + case ISCSI_NET_PARAM_MTU: + case ISCSI_NET_PARAM_PORT: + return S_IRUGO; + default: + return 0; + } } return 0; } +/** + * conn_err_recovery_task - does recovery on all queued sessions + * + * @work: pointer to work struct + * + * iSCSI Session recovery queue manager + */ +static void +#if defined(INIT_DELAYED_WORK_DEFERRABLE) || defined(INIT_WORK_NAR) +conn_err_recovery_task(struct work_struct *work) +#else +conn_err_recovery_task(void *data) +#endif +{ +#if defined(INIT_DELAYED_WORK_DEFERRABLE) || defined(INIT_WORK_NAR) + struct bnx2i_hba *hba = container_of(work, struct bnx2i_hba, + err_rec_task); +#else + struct bnx2i_hba *hba = data; +#endif + int cons_idx = hba->conn_recov_cons_idx; + unsigned int long flags; + struct iscsi_conn *conn; + struct iscsi_session *sess = NULL; + + spin_lock_irqsave(&hba->lock, flags); + while (hba->conn_recov_prod_idx != cons_idx) { + conn = hba->conn_recov_list[cons_idx]; + if (cons_idx == hba->conn_recov_max_idx) + cons_idx = 0; + else + cons_idx++; + spin_unlock_irqrestore(&hba->lock, flags); + if (conn) + sess = conn->session; + if (sess) { + spin_lock_bh(&sess->lock); + if (sess->state != ISCSI_STATE_LOGGING_OUT) { + spin_unlock_bh(&sess->lock); + iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + } else + spin_unlock_bh(&sess->lock); + } + spin_lock_irqsave(&hba->lock, flags); + } + hba->conn_recov_cons_idx = cons_idx; + spin_unlock_irqrestore(&hba->lock, flags); +} + + +static void bnx2i_withdraw_conn_recovery(struct bnx2i_hba *hba, + struct iscsi_conn *conn) +{ + int cons_idx = hba->conn_recov_cons_idx; + unsigned int long flags; + + spin_lock_irqsave(&hba->lock, flags); + while (hba->conn_recov_prod_idx != cons_idx) { + if (conn == hba->conn_recov_list[cons_idx]) { + hba->conn_recov_list[cons_idx] = NULL; + break; + } + if (cons_idx == hba->conn_recov_max_idx) + cons_idx = 0; + else + cons_idx++; + } + spin_unlock_irqrestore(&hba->lock, flags); +} + /* * 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template * used while registering with the scsi host and iSCSI transport module. @@ -2242,16 +2704,25 @@ static struct scsi_host_template bnx2i_host_template = { .queuecommand = iscsi_queuecommand, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler = iscsi_eh_device_reset, +#if (LINUX_VERSION_CODE >= 0x020622) .eh_target_reset_handler = iscsi_eh_recover_target, +#endif .change_queue_depth = iscsi_change_queue_depth, +#if ((defined(__RHELS_DISTRO_6__) && (__RHELS_DISTRO_6__ > 0x0600)) || \ + (defined(__SLES_DISTRO__) && (__SLES_DISTRO__ > 0x1101))) .target_alloc = iscsi_target_alloc, +#endif .can_queue = 2048, .max_sectors = 127, .cmd_per_lun = 128, .this_id = -1, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = ISCSI_MAX_BDS_PER_CMD, +#if (defined(__RHELS_DISTRO_5__)) + .sdev_attrs = bnx2i_dev_attributes, +#else .shost_attrs = bnx2i_dev_attributes, +#endif }; struct iscsi_transport bnx2i_iscsi_transport = { @@ -2266,9 +2737,24 @@ struct iscsi_transport bnx2i_iscsi_transport = { .create_conn = bnx2i_conn_create, .bind_conn = bnx2i_conn_bind, .destroy_conn = bnx2i_conn_destroy, - .attr_is_visible = bnx2i_attr_is_visible, .set_param = iscsi_set_param, + .attr_is_visible = bnx2i_attr_is_visible, +#if ((!defined(__SLES_DISTRO__) || (__SLES_DISTRO__ < 0x1102)) && \ + (!defined(__RHELS_DISTRO_5__) || (__RHELS_DISTRO_5__ < 0x0509)) && \ + (!defined(__RHELS_DISTRO_6__) || (__RHELS_DISTRO_6__ < 0x0602)) && \ + (defined(__DISTRO__) || LINUX_VERSION_CODE < 0x020626)) + .get_conn_param = bnx2i_conn_get_param, +#else .get_conn_param = iscsi_conn_get_param, + .get_ep_param = bnx2i_ep_get_param, +#endif +#if ((defined(__SLES_DISTRO__) && (__SLES_DISTRO__ > 0x1102)) || \ + (defined(__RHELS_DISTRO_5__) && (__RHELS_DISTRO_5__ > 0x0508)) || \ + (defined(__RHELS_DISTRO_6__) && (__RHELS_DISTRO_6__ > 0x0602)) || \ + (!defined(__DISTRO__) && LINUX_VERSION_CODE > 0x020628)) + .set_iface_param = bnx2i_set_iface_param, + .get_iface_param = bnx2i_get_iface_param, +#endif .get_session_param = iscsi_session_get_param, .get_host_param = bnx2i_host_get_param, .start_conn = bnx2i_conn_start, @@ -2277,7 +2763,6 @@ struct iscsi_transport bnx2i_iscsi_transport = { .xmit_task = bnx2i_task_xmit, .get_stats = bnx2i_conn_get_stats, /* TCP connect - disconnect - option-2 interface calls */ - .get_ep_param = bnx2i_ep_get_param, .ep_connect = bnx2i_ep_connect, .ep_poll = bnx2i_ep_poll, .ep_disconnect = bnx2i_ep_disconnect, diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c index c61cf7a43658..5f42f7365a3e 100644 --- a/drivers/scsi/bnx2i/bnx2i_sysfs.c +++ b/drivers/scsi/bnx2i/bnx2i_sysfs.c @@ -20,7 +20,13 @@ */ static inline struct bnx2i_hba *bnx2i_dev_to_hba(struct device *dev) { +#if (defined(__RHELS_DISTRO_5__)) + /* TODO: is the shost_gendev what we want here? or + do we want the actual class_dev */ + struct Scsi_Host *shost = dev_to_shost(dev); +#else struct Scsi_Host *shost = class_to_shost(dev); +#endif return iscsi_host_priv(shost); } -- 2.50.1