Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (170 commits)
  [SCSI] scsi_dh_rdac: Add MD36xxf into device list
  [SCSI] scsi_debug: add consecutive medium errors
  [SCSI] libsas: fix ata list corruption issue
  [SCSI] hpsa: export resettable host attribute
  [SCSI] hpsa: move device attributes to avoid forward declarations
  [SCSI] scsi_debug: Logical Block Provisioning (SBC3r26)
  [SCSI] sd: Logical Block Provisioning update
  [SCSI] Include protection operation in SCSI command trace
  [SCSI] hpsa: fix incorrect PCI IDs and add two new ones (2nd try)
  [SCSI] target: Fix volume size misreporting for volumes > 2TB
  [SCSI] bnx2fc: Broadcom FCoE offload driver
  [SCSI] fcoe: fix broken fcoe interface reset
  [SCSI] fcoe: precedence bug in fcoe_filter_frames()
  [SCSI] libfcoe: Remove stale fcoe-netdev entries
  [SCSI] libfcoe: Move FCOE_MTU definition from fcoe.h to libfcoe.h
  [SCSI] libfc: introduce __fc_fill_fc_hdr that accepts fc_hdr as an argument
  [SCSI] fcoe, libfc: initialize EM anchors list and then update npiv EMs
  [SCSI] Revert "[SCSI] libfc: fix exchange being deleted when the abort itself is timed out"
  [SCSI] libfc: Fixing a memory leak when destroying an interface
  [SCSI] megaraid_sas: Version and Changelog update
  ...

Fix up trivial conflicts due to whitespace differences in
drivers/scsi/libsas/{sas_ata.c,sas_scsi_host.c}
This commit is contained in:
Linus Torvalds 2011-03-17 17:54:40 -07:00
commit c55d267de2
145 changed files with 15391 additions and 2740 deletions

View file

@ -1,3 +1,26 @@
Release Date : Thu. Feb 24, 2011 17:00:00 PST 2010 -
(emaild-id:megaraidlinux@lsi.com)
Adam Radford
Current Version : 00.00.05.34-rc1
Old Version : 00.00.05.29-rc1
1. Fix some failure gotos from megasas_probe_one(), etc.
2. Add missing check_and_restore_queue_depth() call in
complete_cmd_fusion().
3. Enable MSI-X before calling megasas_init_fw().
4. Call tasklet_schedule() even if outbound_intr_status == 0 for MFI based
boards in MSI-X mode.
5. Fix megasas_probe_one() to clear PCI_MSIX_FLAGS_ENABLE in msi control
register in kdump kernel.
6. Fix megasas_get_cmd() to only print "Command pool empty" if
megasas_dbg_lvl is set.
7. Fix megasas_build_dcdb_fusion() to not filter by TYPE_DISK.
8. Fix megasas_build_dcdb_fusion() to use io_request->LUN[1] field.
9. Add MR_EVT_CFG_CLEARED to megasas_aen_polling().
10. Fix tasklet_init() in megasas_init_fw() to use instancet->tasklet.
11. Fix fault state handling in megasas_transition_to_ready().
12. Fix max_sectors setting for IEEE SGL's.
13. Fix iMR OCR support to work correctly.
-------------------------------------------------------------------------------
Release Date : Tues. Dec 14, 2010 17:00:00 PST 2010 -
(emaild-id:megaraidlinux@lsi.com)
Adam Radford

View file

@ -28,6 +28,12 @@ boot parameter "hpsa_allow_any=1" is specified, however these are not tested
nor supported by HP with this driver. For older Smart Arrays, the cciss
driver should still be used.
The "hpsa_simple_mode=1" boot parameter may be used to prevent the driver from
putting the controller into "performant" mode. The difference is that with simple
mode, each command completion requires an interrupt, while with "performant mode"
(the default, and ordinarily better performing) it is possible to have multiple
command completions indicated by a single interrupt.
HPSA specific entries in /sys
-----------------------------
@ -39,6 +45,8 @@ HPSA specific entries in /sys
/sys/class/scsi_host/host*/rescan
/sys/class/scsi_host/host*/firmware_revision
/sys/class/scsi_host/host*/resettable
/sys/class/scsi_host/host*/transport_mode
the host "rescan" attribute is a write only attribute. Writing to this
attribute will cause the driver to scan for new, changed, or removed devices
@ -55,6 +63,21 @@ HPSA specific entries in /sys
root@host:/sys/class/scsi_host/host4# cat firmware_revision
7.14
The transport_mode indicates whether the controller is in "performant"
or "simple" mode. This is controlled by the "hpsa_simple_mode" module
parameter.
The "resettable" read-only attribute indicates whether a particular
controller is able to honor the "reset_devices" kernel parameter. If the
device is resettable, this file will contain a "1", otherwise, a "0". This
parameter is used by kdump, for example, to reset the controller at driver
load time to eliminate any outstanding commands on the controller and get the
controller into a known state so that the kdump initiated i/o will work right
and not be disrupted in any way by stale commands or other stale state
remaining on the controller from the previous kernel. This attribute enables
kexec tools to warn the user if they attempt to designate a device which is
unable to honor the reset_devices kernel parameter as a dump device.
HPSA specific disk attributes:
------------------------------

View file

@ -1343,7 +1343,7 @@ Members of interest:
underruns (overruns should be rare). If possible an LLD
should set 'resid' prior to invoking 'done'. The most
interesting case is data transfers from a SCSI target
device device (i.e. READs) that underrun.
device (e.g. READs) that underrun.
underflow - LLD should place (DID_ERROR << 16) in 'result' if
actual number of bytes transferred is less than this
figure. Not many LLDs implement this check and some that
@ -1351,6 +1351,18 @@ Members of interest:
report a DID_ERROR. Better for an LLD to implement
'resid'.
It is recommended that a LLD set 'resid' on data transfers from a SCSI
target device (e.g. READs). It is especially important that 'resid' is set
when such data transfers have sense keys of MEDIUM ERROR and HARDWARE ERROR
(and possibly RECOVERED ERROR). In these cases if a LLD is in doubt how much
data has been received then the safest approach is to indicate no bytes have
been received. For example: to indicate that no valid data has been received
a LLD might use these helpers:
scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
where 'SCpnt' is a pointer to a scsi_cmnd object. To indicate only three 512
bytes blocks has been received 'resid' could be set like this:
scsi_set_resid(SCpnt, scsi_bufflen(SCpnt) - (3 * 512));
The scsi_cmnd structure is defined in include/scsi/scsi_cmnd.h

View file

@ -5359,8 +5359,7 @@ S: Supported
F: drivers/s390/crypto/
S390 ZFCP DRIVER
M: Christof Schmitt <christof.schmitt@de.ibm.com>
M: Swen Schillig <swen@vnet.ibm.com>
M: Steffen Maier <maier@linux.vnet.ibm.com>
M: linux390@de.ibm.com
L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/

View file

@ -2045,9 +2045,26 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
if (error && req->cmd_type == REQ_TYPE_FS &&
!(req->cmd_flags & REQ_QUIET)) {
printk(KERN_ERR "end_request: I/O error, dev %s, sector %llu\n",
req->rq_disk ? req->rq_disk->disk_name : "?",
(unsigned long long)blk_rq_pos(req));
char *error_type;
switch (error) {
case -ENOLINK:
error_type = "recoverable transport";
break;
case -EREMOTEIO:
error_type = "critical target";
break;
case -EBADE:
error_type = "critical nexus";
break;
case -EIO:
default:
error_type = "I/O";
break;
}
printk(KERN_ERR "end_request: %s error, dev %s, sector %llu\n",
error_type, req->rq_disk ? req->rq_disk->disk_name : "?",
(unsigned long long)blk_rq_pos(req));
}
blk_account_io_completion(req, nr_bytes);

View file

@ -532,6 +532,29 @@ iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *s
stats->custom[3].value = conn->fmr_unalign_cnt;
}
static int iscsi_iser_get_ep_param(struct iscsi_endpoint *ep,
enum iscsi_param param, char *buf)
{
struct iser_conn *ib_conn = ep->dd_data;
int len;
switch (param) {
case ISCSI_PARAM_CONN_PORT:
case ISCSI_PARAM_CONN_ADDRESS:
if (!ib_conn || !ib_conn->cma_id)
return -ENOTCONN;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
&ib_conn->cma_id->route.addr.dst_addr,
param, buf);
break;
default:
return -ENOSYS;
}
return len;
}
static struct iscsi_endpoint *
iscsi_iser_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
int non_blocking)
@ -637,6 +660,8 @@ static struct iscsi_transport iscsi_iser_transport = {
ISCSI_MAX_BURST |
ISCSI_PDU_INORDER_EN |
ISCSI_DATASEQ_INORDER_EN |
ISCSI_CONN_PORT |
ISCSI_CONN_ADDRESS |
ISCSI_EXP_STATSN |
ISCSI_PERSISTENT_PORT |
ISCSI_PERSISTENT_ADDRESS |
@ -659,6 +684,7 @@ static struct iscsi_transport iscsi_iser_transport = {
.destroy_conn = iscsi_iser_conn_destroy,
.set_param = iscsi_iser_set_param,
.get_conn_param = iscsi_conn_get_param,
.get_ep_param = iscsi_iser_get_ep_param,
.get_session_param = iscsi_session_get_param,
.start_conn = iscsi_iser_conn_start,
.stop_conn = iscsi_iser_conn_stop,

View file

@ -1283,24 +1283,22 @@ static int do_end_io(struct multipath *m, struct request *clone,
if (!error && !clone->errors)
return 0; /* I/O complete */
if (error == -EOPNOTSUPP)
return error;
if (clone->cmd_flags & REQ_DISCARD)
/*
* Pass all discard request failures up.
* FIXME: only fail_path if the discard failed due to a
* transport problem. This requires precise understanding
* of the underlying failure (e.g. the SCSI sense).
*/
if (error == -EOPNOTSUPP || error == -EREMOTEIO)
return error;
if (mpio->pgpath)
fail_path(mpio->pgpath);
spin_lock_irqsave(&m->lock, flags);
if (!m->nr_valid_paths && !m->queue_if_no_path && !__must_push_back(m))
r = -EIO;
if (!m->nr_valid_paths) {
if (!m->queue_if_no_path) {
if (!__must_push_back(m))
r = -EIO;
} else {
if (error == -EBADE)
r = error;
}
}
spin_unlock_irqrestore(&m->lock, flags);
return r;

View file

@ -2593,6 +2593,7 @@ typedef struct _CONFIG_PAGE_SAS_IO_UNIT_0
#define MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE (0x03)
#define MPI_SAS_IOUNIT0_RATE_1_5 (0x08)
#define MPI_SAS_IOUNIT0_RATE_3_0 (0x09)
#define MPI_SAS_IOUNIT0_RATE_6_0 (0x0A)
/* see mpi_sas.h for values for SAS IO Unit Page 0 ControllerPhyDeviceInfo values */

View file

@ -841,6 +841,7 @@ typedef struct _EVENT_DATA_SAS_PHY_LINK_STATUS
#define MPI_EVENT_SAS_PLS_LR_RATE_SATA_OOB_COMPLETE (0x03)
#define MPI_EVENT_SAS_PLS_LR_RATE_1_5 (0x08)
#define MPI_EVENT_SAS_PLS_LR_RATE_3_0 (0x09)
#define MPI_EVENT_SAS_PLS_LR_RATE_6_0 (0x0A)
/* SAS Discovery Event data */

View file

@ -7418,7 +7418,12 @@ mpt_display_event_info(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply)
case MPI_EVENT_SAS_PLS_LR_RATE_3_0:
snprintf(evStr, EVENT_DESCR_STR_SZ,
"SAS PHY Link Status: Phy=%d:"
" Rate 3.0 Gpbs",PhyNumber);
" Rate 3.0 Gbps", PhyNumber);
break;
case MPI_EVENT_SAS_PLS_LR_RATE_6_0:
snprintf(evStr, EVENT_DESCR_STR_SZ,
"SAS PHY Link Status: Phy=%d:"
" Rate 6.0 Gbps", PhyNumber);
break;
default:
snprintf(evStr, EVENT_DESCR_STR_SZ,

View file

@ -1314,8 +1314,10 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size)
else
karg->adapterType = MPT_IOCTL_INTERFACE_SCSI;
if (karg->hdr.port > 1)
if (karg->hdr.port > 1) {
kfree(karg);
return -EINVAL;
}
port = karg->hdr.port;
karg->port = port;

View file

@ -1973,7 +1973,6 @@ static struct scsi_host_template mptsas_driver_template = {
.change_queue_depth = mptscsih_change_queue_depth,
.eh_abort_handler = mptscsih_abort,
.eh_device_reset_handler = mptscsih_dev_reset,
.eh_bus_reset_handler = mptscsih_bus_reset,
.eh_host_reset_handler = mptscsih_host_reset,
.bios_param = mptscsih_bios_param,
.can_queue = MPT_SAS_CAN_QUEUE,
@ -3063,6 +3062,9 @@ static int mptsas_probe_one_phy(struct device *dev,
case MPI_SAS_IOUNIT0_RATE_3_0:
phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
break;
case MPI_SAS_IOUNIT0_RATE_6_0:
phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
break;
case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE:
case MPI_SAS_IOUNIT0_RATE_UNKNOWN:
default:
@ -3691,7 +3693,8 @@ mptsas_send_link_status_event(struct fw_event_work *fw_event)
}
if (link_rate == MPI_SAS_IOUNIT0_RATE_1_5 ||
link_rate == MPI_SAS_IOUNIT0_RATE_3_0) {
link_rate == MPI_SAS_IOUNIT0_RATE_3_0 ||
link_rate == MPI_SAS_IOUNIT0_RATE_6_0) {
if (!port_info) {
if (ioc->old_sas_discovery_protocal) {

View file

@ -145,13 +145,6 @@ static struct {
{ "Broadcom NetXtreme II BCM57712E XGb" }
};
#ifndef PCI_DEVICE_ID_NX2_57712
#define PCI_DEVICE_ID_NX2_57712 0x1662
#endif
#ifndef PCI_DEVICE_ID_NX2_57712E
#define PCI_DEVICE_ID_NX2_57712E 0x1663
#endif
static DEFINE_PCI_DEVICE_TABLE(bnx2x_pci_tbl) = {
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57710), BCM57710 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711), BCM57711 },

View file

@ -122,36 +122,21 @@ static int __init zfcp_module_init(void)
{
int retval = -ENOMEM;
zfcp_data.gpn_ft_cache = zfcp_cache_hw_align("zfcp_gpn",
sizeof(struct zfcp_fc_gpn_ft_req));
if (!zfcp_data.gpn_ft_cache)
goto out;
zfcp_data.qtcb_cache = zfcp_cache_hw_align("zfcp_qtcb",
sizeof(struct fsf_qtcb));
if (!zfcp_data.qtcb_cache)
zfcp_fsf_qtcb_cache = zfcp_cache_hw_align("zfcp_fsf_qtcb",
sizeof(struct fsf_qtcb));
if (!zfcp_fsf_qtcb_cache)
goto out_qtcb_cache;
zfcp_data.sr_buffer_cache = zfcp_cache_hw_align("zfcp_sr",
sizeof(struct fsf_status_read_buffer));
if (!zfcp_data.sr_buffer_cache)
goto out_sr_cache;
zfcp_fc_req_cache = zfcp_cache_hw_align("zfcp_fc_req",
sizeof(struct zfcp_fc_req));
if (!zfcp_fc_req_cache)
goto out_fc_cache;
zfcp_data.gid_pn_cache = zfcp_cache_hw_align("zfcp_gid",
sizeof(struct zfcp_fc_gid_pn));
if (!zfcp_data.gid_pn_cache)
goto out_gid_cache;
zfcp_data.adisc_cache = zfcp_cache_hw_align("zfcp_adisc",
sizeof(struct zfcp_fc_els_adisc));
if (!zfcp_data.adisc_cache)
goto out_adisc_cache;
zfcp_data.scsi_transport_template =
zfcp_scsi_transport_template =
fc_attach_transport(&zfcp_transport_functions);
if (!zfcp_data.scsi_transport_template)
if (!zfcp_scsi_transport_template)
goto out_transport;
scsi_transport_reserve_device(zfcp_data.scsi_transport_template,
scsi_transport_reserve_device(zfcp_scsi_transport_template,
sizeof(struct zfcp_scsi_dev));
@ -175,18 +160,12 @@ static int __init zfcp_module_init(void)
out_ccw_register:
misc_deregister(&zfcp_cfdc_misc);
out_misc:
fc_release_transport(zfcp_data.scsi_transport_template);
fc_release_transport(zfcp_scsi_transport_template);
out_transport:
kmem_cache_destroy(zfcp_data.adisc_cache);
out_adisc_cache:
kmem_cache_destroy(zfcp_data.gid_pn_cache);
out_gid_cache:
kmem_cache_destroy(zfcp_data.sr_buffer_cache);
out_sr_cache:
kmem_cache_destroy(zfcp_data.qtcb_cache);
kmem_cache_destroy(zfcp_fc_req_cache);
out_fc_cache:
kmem_cache_destroy(zfcp_fsf_qtcb_cache);
out_qtcb_cache:
kmem_cache_destroy(zfcp_data.gpn_ft_cache);
out:
return retval;
}
@ -196,12 +175,9 @@ static void __exit zfcp_module_exit(void)
{
ccw_driver_unregister(&zfcp_ccw_driver);
misc_deregister(&zfcp_cfdc_misc);
fc_release_transport(zfcp_data.scsi_transport_template);
kmem_cache_destroy(zfcp_data.adisc_cache);
kmem_cache_destroy(zfcp_data.gid_pn_cache);
kmem_cache_destroy(zfcp_data.sr_buffer_cache);
kmem_cache_destroy(zfcp_data.qtcb_cache);
kmem_cache_destroy(zfcp_data.gpn_ft_cache);
fc_release_transport(zfcp_scsi_transport_template);
kmem_cache_destroy(zfcp_fc_req_cache);
kmem_cache_destroy(zfcp_fsf_qtcb_cache);
}
module_exit(zfcp_module_exit);
@ -260,18 +236,18 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
return -ENOMEM;
adapter->pool.qtcb_pool =
mempool_create_slab_pool(4, zfcp_data.qtcb_cache);
mempool_create_slab_pool(4, zfcp_fsf_qtcb_cache);
if (!adapter->pool.qtcb_pool)
return -ENOMEM;
adapter->pool.status_read_data =
mempool_create_slab_pool(FSF_STATUS_READS_RECOM,
zfcp_data.sr_buffer_cache);
if (!adapter->pool.status_read_data)
BUILD_BUG_ON(sizeof(struct fsf_status_read_buffer) > PAGE_SIZE);
adapter->pool.sr_data =
mempool_create_page_pool(FSF_STATUS_READS_RECOM, 0);
if (!adapter->pool.sr_data)
return -ENOMEM;
adapter->pool.gid_pn =
mempool_create_slab_pool(1, zfcp_data.gid_pn_cache);
mempool_create_slab_pool(1, zfcp_fc_req_cache);
if (!adapter->pool.gid_pn)
return -ENOMEM;
@ -290,8 +266,8 @@ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
mempool_destroy(adapter->pool.qtcb_pool);
if (adapter->pool.status_read_req)
mempool_destroy(adapter->pool.status_read_req);
if (adapter->pool.status_read_data)
mempool_destroy(adapter->pool.status_read_data);
if (adapter->pool.sr_data)
mempool_destroy(adapter->pool.sr_data);
if (adapter->pool.gid_pn)
mempool_destroy(adapter->pool.gid_pn);
}
@ -386,6 +362,7 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports);
INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update);
if (zfcp_qdio_setup(adapter))
goto failed;
@ -437,7 +414,7 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
adapter->dma_parms.max_segment_size = ZFCP_QDIO_SBALE_LEN;
adapter->ccw_device->dev.dma_parms = &adapter->dma_parms;
if (!zfcp_adapter_scsi_register(adapter))
if (!zfcp_scsi_adapter_register(adapter))
return adapter;
failed:
@ -451,10 +428,11 @@ void zfcp_adapter_unregister(struct zfcp_adapter *adapter)
cancel_work_sync(&adapter->scan_work);
cancel_work_sync(&adapter->stat_work);
cancel_work_sync(&adapter->ns_up_work);
zfcp_destroy_adapter_work_queue(adapter);
zfcp_fc_wka_ports_force_offline(adapter->gs);
zfcp_adapter_scsi_unregister(adapter);
zfcp_scsi_adapter_unregister(adapter);
sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs);
zfcp_erp_thread_kill(adapter);

View file

@ -89,7 +89,6 @@ struct zfcp_reqlist;
#define ZFCP_STATUS_LUN_READONLY 0x00000008
/* FSF request status (this does not have a common part) */
#define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT 0x00000002
#define ZFCP_STATUS_FSFREQ_ERROR 0x00000008
#define ZFCP_STATUS_FSFREQ_CLEANUP 0x00000010
#define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED 0x00000040
@ -108,7 +107,7 @@ struct zfcp_adapter_mempool {
mempool_t *scsi_req;
mempool_t *scsi_abort;
mempool_t *status_read_req;
mempool_t *status_read_data;
mempool_t *sr_data;
mempool_t *gid_pn;
mempool_t *qtcb_pool;
};
@ -190,6 +189,7 @@ struct zfcp_adapter {
struct fsf_qtcb_bottom_port *stats_reset_data;
unsigned long stats_reset;
struct work_struct scan_work;
struct work_struct ns_up_work;
struct service_level service_level;
struct workqueue_struct *work_queue;
struct device_dma_parameters dma_parms;
@ -314,15 +314,4 @@ struct zfcp_fsf_req {
void (*handler)(struct zfcp_fsf_req *);
};
/* driver data */
struct zfcp_data {
struct scsi_host_template scsi_host_template;
struct scsi_transport_template *scsi_transport_template;
struct kmem_cache *gpn_ft_cache;
struct kmem_cache *qtcb_cache;
struct kmem_cache *sr_buffer_cache;
struct kmem_cache *gid_pn_cache;
struct kmem_cache *adisc_cache;
};
#endif /* ZFCP_DEF_H */

View file

@ -732,7 +732,7 @@ static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *act)
if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED)
return ZFCP_ERP_FAILED;
if (mempool_resize(act->adapter->pool.status_read_data,
if (mempool_resize(act->adapter->pool.sr_data,
act->adapter->stat_read_buf_num, GFP_KERNEL))
return ZFCP_ERP_FAILED;
@ -1231,8 +1231,10 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
if (result == ZFCP_ERP_SUCCEEDED) {
register_service_level(&adapter->service_level);
queue_work(adapter->work_queue, &adapter->scan_work);
queue_work(adapter->work_queue, &adapter->ns_up_work);
} else
unregister_service_level(&adapter->service_level);
kref_put(&adapter->ref, zfcp_adapter_release);
break;
}

View file

@ -80,6 +80,7 @@ extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long);
extern void zfcp_erp_timeout_handler(unsigned long);
/* zfcp_fc.c */
extern struct kmem_cache *zfcp_fc_req_cache;
extern void zfcp_fc_enqueue_event(struct zfcp_adapter *,
enum fc_host_event_code event_code, u32);
extern void zfcp_fc_post_event(struct work_struct *);
@ -95,8 +96,10 @@ extern int zfcp_fc_gs_setup(struct zfcp_adapter *);
extern void zfcp_fc_gs_destroy(struct zfcp_adapter *);
extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *);
extern int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *);
extern void zfcp_fc_sym_name_update(struct work_struct *);
/* zfcp_fsf.c */
extern struct kmem_cache *zfcp_fsf_qtcb_cache;
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
extern int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *);
extern int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *);
@ -139,9 +142,9 @@ extern struct zfcp_fsf_req *zfcp_fsf_get_req(struct zfcp_qdio *,
struct qdio_buffer *);
/* zfcp_scsi.c */
extern struct zfcp_data zfcp_data;
extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
extern struct scsi_transport_template *zfcp_scsi_transport_template;
extern int zfcp_scsi_adapter_register(struct zfcp_adapter *);
extern void zfcp_scsi_adapter_unregister(struct zfcp_adapter *);
extern struct fc_function_template zfcp_transport_functions;
extern void zfcp_scsi_rport_work(struct work_struct *);
extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *);

View file

@ -11,11 +11,14 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/utsname.h>
#include <scsi/fc/fc_els.h>
#include <scsi/libfc.h>
#include "zfcp_ext.h"
#include "zfcp_fc.h"
struct kmem_cache *zfcp_fc_req_cache;
static u32 zfcp_fc_rscn_range_mask[] = {
[ELS_ADDR_FMT_PORT] = 0xFFFFFF,
[ELS_ADDR_FMT_AREA] = 0xFFFF00,
@ -260,24 +263,18 @@ void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
zfcp_fc_incoming_rscn(fsf_req);
}
static void zfcp_fc_ns_gid_pn_eval(void *data)
static void zfcp_fc_ns_gid_pn_eval(struct zfcp_fc_req *fc_req)
{
struct zfcp_fc_gid_pn *gid_pn = data;
struct zfcp_fsf_ct_els *ct = &gid_pn->ct;
struct zfcp_fc_gid_pn_req *gid_pn_req = sg_virt(ct->req);
struct zfcp_fc_gid_pn_resp *gid_pn_resp = sg_virt(ct->resp);
struct zfcp_port *port = gid_pn->port;
struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els;
struct zfcp_fc_gid_pn_rsp *gid_pn_rsp = &fc_req->u.gid_pn.rsp;
if (ct->status)
if (ct_els->status)
return;
if (gid_pn_resp->ct_hdr.ct_cmd != FC_FS_ACC)
if (gid_pn_rsp->ct_hdr.ct_cmd != FC_FS_ACC)
return;
/* paranoia */
if (gid_pn_req->gid_pn.fn_wwpn != port->wwpn)
return;
/* looks like a valid d_id */
port->d_id = ntoh24(gid_pn_resp->gid_pn.fp_fid);
ct_els->port->d_id = ntoh24(gid_pn_rsp->gid_pn.fp_fid);
}
static void zfcp_fc_complete(void *data)
@ -285,69 +282,73 @@ static void zfcp_fc_complete(void *data)
complete(data);
}
static void zfcp_fc_ct_ns_init(struct fc_ct_hdr *ct_hdr, u16 cmd, u16 mr_size)
{
ct_hdr->ct_rev = FC_CT_REV;
ct_hdr->ct_fs_type = FC_FST_DIR;
ct_hdr->ct_fs_subtype = FC_NS_SUBTYPE;
ct_hdr->ct_cmd = cmd;
ct_hdr->ct_mr_size = mr_size / 4;
}
static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port,
struct zfcp_fc_gid_pn *gid_pn)
struct zfcp_fc_req *fc_req)
{
struct zfcp_adapter *adapter = port->adapter;
DECLARE_COMPLETION_ONSTACK(completion);
struct zfcp_fc_gid_pn_req *gid_pn_req = &fc_req->u.gid_pn.req;
struct zfcp_fc_gid_pn_rsp *gid_pn_rsp = &fc_req->u.gid_pn.rsp;
int ret;
/* setup parameters for send generic command */
gid_pn->port = port;
gid_pn->ct.handler = zfcp_fc_complete;
gid_pn->ct.handler_data = &completion;
gid_pn->ct.req = &gid_pn->sg_req;
gid_pn->ct.resp = &gid_pn->sg_resp;
sg_init_one(&gid_pn->sg_req, &gid_pn->gid_pn_req,
sizeof(struct zfcp_fc_gid_pn_req));
sg_init_one(&gid_pn->sg_resp, &gid_pn->gid_pn_resp,
sizeof(struct zfcp_fc_gid_pn_resp));
fc_req->ct_els.port = port;
fc_req->ct_els.handler = zfcp_fc_complete;
fc_req->ct_els.handler_data = &completion;
fc_req->ct_els.req = &fc_req->sg_req;
fc_req->ct_els.resp = &fc_req->sg_rsp;
sg_init_one(&fc_req->sg_req, gid_pn_req, sizeof(*gid_pn_req));
sg_init_one(&fc_req->sg_rsp, gid_pn_rsp, sizeof(*gid_pn_rsp));
/* setup nameserver request */
gid_pn->gid_pn_req.ct_hdr.ct_rev = FC_CT_REV;
gid_pn->gid_pn_req.ct_hdr.ct_fs_type = FC_FST_DIR;
gid_pn->gid_pn_req.ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE;
gid_pn->gid_pn_req.ct_hdr.ct_options = 0;
gid_pn->gid_pn_req.ct_hdr.ct_cmd = FC_NS_GID_PN;
gid_pn->gid_pn_req.ct_hdr.ct_mr_size = ZFCP_FC_CT_SIZE_PAGE / 4;
gid_pn->gid_pn_req.gid_pn.fn_wwpn = port->wwpn;
zfcp_fc_ct_ns_init(&gid_pn_req->ct_hdr,
FC_NS_GID_PN, ZFCP_FC_CT_SIZE_PAGE);
gid_pn_req->gid_pn.fn_wwpn = port->wwpn;
ret = zfcp_fsf_send_ct(&adapter->gs->ds, &gid_pn->ct,
ret = zfcp_fsf_send_ct(&adapter->gs->ds, &fc_req->ct_els,
adapter->pool.gid_pn_req,
ZFCP_FC_CTELS_TMO);
if (!ret) {
wait_for_completion(&completion);
zfcp_fc_ns_gid_pn_eval(gid_pn);
zfcp_fc_ns_gid_pn_eval(fc_req);
}
return ret;
}
/**
* zfcp_fc_ns_gid_pn_request - initiate GID_PN nameserver request
* zfcp_fc_ns_gid_pn - initiate GID_PN nameserver request
* @port: port where GID_PN request is needed
* return: -ENOMEM on error, 0 otherwise
*/
static int zfcp_fc_ns_gid_pn(struct zfcp_port *port)
{
int ret;
struct zfcp_fc_gid_pn *gid_pn;
struct zfcp_fc_req *fc_req;
struct zfcp_adapter *adapter = port->adapter;
gid_pn = mempool_alloc(adapter->pool.gid_pn, GFP_ATOMIC);
if (!gid_pn)
fc_req = mempool_alloc(adapter->pool.gid_pn, GFP_ATOMIC);
if (!fc_req)
return -ENOMEM;
memset(gid_pn, 0, sizeof(*gid_pn));
memset(fc_req, 0, sizeof(*fc_req));
ret = zfcp_fc_wka_port_get(&adapter->gs->ds);
if (ret)
goto out;
ret = zfcp_fc_ns_gid_pn_request(port, gid_pn);
ret = zfcp_fc_ns_gid_pn_request(port, fc_req);
zfcp_fc_wka_port_put(&adapter->gs->ds);
out:
mempool_free(gid_pn, adapter->pool.gid_pn);
mempool_free(fc_req, adapter->pool.gid_pn);
return ret;
}
@ -419,11 +420,11 @@ void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fc_els_flogi *plogi)
static void zfcp_fc_adisc_handler(void *data)
{
struct zfcp_fc_els_adisc *adisc = data;
struct zfcp_port *port = adisc->els.port;
struct fc_els_adisc *adisc_resp = &adisc->adisc_resp;
struct zfcp_fc_req *fc_req = data;
struct zfcp_port *port = fc_req->ct_els.port;
struct fc_els_adisc *adisc_resp = &fc_req->u.adisc.rsp;
if (adisc->els.status) {
if (fc_req->ct_els.status) {
/* request rejected or timed out */
zfcp_erp_port_forced_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED,
"fcadh_1");
@ -445,42 +446,42 @@ static void zfcp_fc_adisc_handler(void *data)
out:
atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
put_device(&port->dev);
kmem_cache_free(zfcp_data.adisc_cache, adisc);
kmem_cache_free(zfcp_fc_req_cache, fc_req);
}
static int zfcp_fc_adisc(struct zfcp_port *port)
{
struct zfcp_fc_els_adisc *adisc;
struct zfcp_fc_req *fc_req;
struct zfcp_adapter *adapter = port->adapter;
struct Scsi_Host *shost = adapter->scsi_host;
int ret;
adisc = kmem_cache_zalloc(zfcp_data.adisc_cache, GFP_ATOMIC);
if (!adisc)
fc_req = kmem_cache_zalloc(zfcp_fc_req_cache, GFP_ATOMIC);
if (!fc_req)
return -ENOMEM;
adisc->els.port = port;
adisc->els.req = &adisc->req;
adisc->els.resp = &adisc->resp;
sg_init_one(adisc->els.req, &adisc->adisc_req,
fc_req->ct_els.port = port;
fc_req->ct_els.req = &fc_req->sg_req;
fc_req->ct_els.resp = &fc_req->sg_rsp;
sg_init_one(&fc_req->sg_req, &fc_req->u.adisc.req,
sizeof(struct fc_els_adisc));
sg_init_one(adisc->els.resp, &adisc->adisc_resp,
sg_init_one(&fc_req->sg_rsp, &fc_req->u.adisc.rsp,
sizeof(struct fc_els_adisc));
adisc->els.handler = zfcp_fc_adisc_handler;
adisc->els.handler_data = adisc;
fc_req->ct_els.handler = zfcp_fc_adisc_handler;
fc_req->ct_els.handler_data = fc_req;
/* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
without FC-AL-2 capability, so we don't set it */
adisc->adisc_req.adisc_wwpn = fc_host_port_name(adapter->scsi_host);
adisc->adisc_req.adisc_wwnn = fc_host_node_name(adapter->scsi_host);
adisc->adisc_req.adisc_cmd = ELS_ADISC;
hton24(adisc->adisc_req.adisc_port_id,
fc_host_port_id(adapter->scsi_host));
fc_req->u.adisc.req.adisc_wwpn = fc_host_port_name(shost);
fc_req->u.adisc.req.adisc_wwnn = fc_host_node_name(shost);
fc_req->u.adisc.req.adisc_cmd = ELS_ADISC;
hton24(fc_req->u.adisc.req.adisc_port_id, fc_host_port_id(shost));
ret = zfcp_fsf_send_els(adapter, port->d_id, &adisc->els,
ret = zfcp_fsf_send_els(adapter, port->d_id, &fc_req->ct_els,
ZFCP_FC_CTELS_TMO);
if (ret)
kmem_cache_free(zfcp_data.adisc_cache, adisc);
kmem_cache_free(zfcp_fc_req_cache, fc_req);
return ret;
}
@ -528,68 +529,42 @@ void zfcp_fc_test_link(struct zfcp_port *port)
put_device(&port->dev);
}
static void zfcp_free_sg_env(struct zfcp_fc_gpn_ft *gpn_ft, int buf_num)
static struct zfcp_fc_req *zfcp_alloc_sg_env(int buf_num)
{
struct scatterlist *sg = &gpn_ft->sg_req;
struct zfcp_fc_req *fc_req;
kmem_cache_free(zfcp_data.gpn_ft_cache, sg_virt(sg));
zfcp_sg_free_table(gpn_ft->sg_resp, buf_num);
kfree(gpn_ft);
}
static struct zfcp_fc_gpn_ft *zfcp_alloc_sg_env(int buf_num)
{
struct zfcp_fc_gpn_ft *gpn_ft;
struct zfcp_fc_gpn_ft_req *req;
gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL);
if (!gpn_ft)
fc_req = kmem_cache_zalloc(zfcp_fc_req_cache, GFP_KERNEL);
if (!fc_req)
return NULL;
req = kmem_cache_zalloc(zfcp_data.gpn_ft_cache, GFP_KERNEL);
if (!req) {
kfree(gpn_ft);
gpn_ft = NULL;
goto out;
if (zfcp_sg_setup_table(&fc_req->sg_rsp, buf_num)) {
kmem_cache_free(zfcp_fc_req_cache, fc_req);
return NULL;
}
sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
if (zfcp_sg_setup_table(gpn_ft->sg_resp, buf_num)) {
zfcp_free_sg_env(gpn_ft, buf_num);
gpn_ft = NULL;
}
out:
return gpn_ft;
sg_init_one(&fc_req->sg_req, &fc_req->u.gpn_ft.req,
sizeof(struct zfcp_fc_gpn_ft_req));
return fc_req;
}
static int zfcp_fc_send_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft,
static int zfcp_fc_send_gpn_ft(struct zfcp_fc_req *fc_req,
struct zfcp_adapter *adapter, int max_bytes)
{
struct zfcp_fsf_ct_els *ct = &gpn_ft->ct;
struct zfcp_fc_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els;
struct zfcp_fc_gpn_ft_req *req = &fc_req->u.gpn_ft.req;
DECLARE_COMPLETION_ONSTACK(completion);
int ret;
/* prepare CT IU for GPN_FT */
req->ct_hdr.ct_rev = FC_CT_REV;
req->ct_hdr.ct_fs_type = FC_FST_DIR;
req->ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE;
req->ct_hdr.ct_options = 0;
req->ct_hdr.ct_cmd = FC_NS_GPN_FT;
req->ct_hdr.ct_mr_size = max_bytes / 4;
req->gpn_ft.fn_domain_id_scope = 0;
req->gpn_ft.fn_area_id_scope = 0;
zfcp_fc_ct_ns_init(&req->ct_hdr, FC_NS_GPN_FT, max_bytes);
req->gpn_ft.fn_fc4_type = FC_TYPE_FCP;
/* prepare zfcp_send_ct */
ct->handler = zfcp_fc_complete;
ct->handler_data = &completion;
ct->req = &gpn_ft->sg_req;
ct->resp = gpn_ft->sg_resp;
ct_els->handler = zfcp_fc_complete;
ct_els->handler_data = &completion;
ct_els->req = &fc_req->sg_req;
ct_els->resp = &fc_req->sg_rsp;
ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct, NULL,
ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct_els, NULL,
ZFCP_FC_CTELS_TMO);
if (!ret)
wait_for_completion(&completion);
@ -610,11 +585,11 @@ static void zfcp_fc_validate_port(struct zfcp_port *port, struct list_head *lh)
list_move_tail(&port->list, lh);
}
static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft,
static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_req *fc_req,
struct zfcp_adapter *adapter, int max_entries)
{
struct zfcp_fsf_ct_els *ct = &gpn_ft->ct;
struct scatterlist *sg = gpn_ft->sg_resp;
struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els;
struct scatterlist *sg = &fc_req->sg_rsp;
struct fc_ct_hdr *hdr = sg_virt(sg);
struct fc_gpn_ft_resp *acc = sg_virt(sg);
struct zfcp_port *port, *tmp;
@ -623,7 +598,7 @@ static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft,
u32 d_id;
int ret = 0, x, last = 0;
if (ct->status)
if (ct_els->status)
return -EIO;
if (hdr->ct_cmd != FC_FS_ACC) {
@ -687,7 +662,7 @@ void zfcp_fc_scan_ports(struct work_struct *work)
struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter,
scan_work);
int ret, i;
struct zfcp_fc_gpn_ft *gpn_ft;
struct zfcp_fc_req *fc_req;
int chain, max_entries, buf_num, max_bytes;
chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
@ -702,25 +677,145 @@ void zfcp_fc_scan_ports(struct work_struct *work)
if (zfcp_fc_wka_port_get(&adapter->gs->ds))
return;
gpn_ft = zfcp_alloc_sg_env(buf_num);
if (!gpn_ft)
fc_req = zfcp_alloc_sg_env(buf_num);
if (!fc_req)
goto out;
for (i = 0; i < 3; i++) {
ret = zfcp_fc_send_gpn_ft(gpn_ft, adapter, max_bytes);
ret = zfcp_fc_send_gpn_ft(fc_req, adapter, max_bytes);
if (!ret) {
ret = zfcp_fc_eval_gpn_ft(gpn_ft, adapter, max_entries);
ret = zfcp_fc_eval_gpn_ft(fc_req, adapter, max_entries);
if (ret == -EAGAIN)
ssleep(1);
else
break;
}
}
zfcp_free_sg_env(gpn_ft, buf_num);
zfcp_sg_free_table(&fc_req->sg_rsp, buf_num);
kmem_cache_free(zfcp_fc_req_cache, fc_req);
out:
zfcp_fc_wka_port_put(&adapter->gs->ds);
}
static int zfcp_fc_gspn(struct zfcp_adapter *adapter,
struct zfcp_fc_req *fc_req)
{
DECLARE_COMPLETION_ONSTACK(completion);
char devno[] = "DEVNO:";
struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els;
struct zfcp_fc_gspn_req *gspn_req = &fc_req->u.gspn.req;
struct zfcp_fc_gspn_rsp *gspn_rsp = &fc_req->u.gspn.rsp;
int ret;
zfcp_fc_ct_ns_init(&gspn_req->ct_hdr, FC_NS_GSPN_ID,
FC_SYMBOLIC_NAME_SIZE);
hton24(gspn_req->gspn.fp_fid, fc_host_port_id(adapter->scsi_host));
sg_init_one(&fc_req->sg_req, gspn_req, sizeof(*gspn_req));
sg_init_one(&fc_req->sg_rsp, gspn_rsp, sizeof(*gspn_rsp));
ct_els->handler = zfcp_fc_complete;
ct_els->handler_data = &completion;
ct_els->req = &fc_req->sg_req;
ct_els->resp = &fc_req->sg_rsp;
ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct_els, NULL,
ZFCP_FC_CTELS_TMO);
if (ret)
return ret;
wait_for_completion(&completion);
if (ct_els->status)
return ct_els->status;
if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_NPIV &&
!(strstr(gspn_rsp->gspn.fp_name, devno)))
snprintf(fc_host_symbolic_name(adapter->scsi_host),
FC_SYMBOLIC_NAME_SIZE, "%s%s %s NAME: %s",
gspn_rsp->gspn.fp_name, devno,
dev_name(&adapter->ccw_device->dev),
init_utsname()->nodename);
else
strlcpy(fc_host_symbolic_name(adapter->scsi_host),
gspn_rsp->gspn.fp_name, FC_SYMBOLIC_NAME_SIZE);
return 0;
}
static void zfcp_fc_rspn(struct zfcp_adapter *adapter,
struct zfcp_fc_req *fc_req)
{
DECLARE_COMPLETION_ONSTACK(completion);
struct Scsi_Host *shost = adapter->scsi_host;
struct zfcp_fsf_ct_els *ct_els = &fc_req->ct_els;
struct zfcp_fc_rspn_req *rspn_req = &fc_req->u.rspn.req;
struct fc_ct_hdr *rspn_rsp = &fc_req->u.rspn.rsp;
int ret, len;
zfcp_fc_ct_ns_init(&rspn_req->ct_hdr, FC_NS_RSPN_ID,
FC_SYMBOLIC_NAME_SIZE);
hton24(rspn_req->rspn.fr_fid.fp_fid, fc_host_port_id(shost));
len = strlcpy(rspn_req->rspn.fr_name, fc_host_symbolic_name(shost),
FC_SYMBOLIC_NAME_SIZE);
rspn_req->rspn.fr_name_len = len;
sg_init_one(&fc_req->sg_req, rspn_req, sizeof(*rspn_req));
sg_init_one(&fc_req->sg_rsp, rspn_rsp, sizeof(*rspn_rsp));
ct_els->handler = zfcp_fc_complete;
ct_els->handler_data = &completion;
ct_els->req = &fc_req->sg_req;
ct_els->resp = &fc_req->sg_rsp;
ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct_els, NULL,
ZFCP_FC_CTELS_TMO);
if (!ret)
wait_for_completion(&completion);
}
/**
* zfcp_fc_sym_name_update - Retrieve and update the symbolic port name
* @work: ns_up_work of the adapter where to update the symbolic port name
*
* Retrieve the current symbolic port name that may have been set by
* the hardware using the GSPN request and update the fc_host
* symbolic_name sysfs attribute. When running in NPIV mode (and hence
* the port name is unique for this system), update the symbolic port
* name to add Linux specific information and update the FC nameserver
* using the RSPN request.
*/
void zfcp_fc_sym_name_update(struct work_struct *work)
{
struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter,
ns_up_work);
int ret;
struct zfcp_fc_req *fc_req;
if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT &&
fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV)
return;
fc_req = kmem_cache_zalloc(zfcp_fc_req_cache, GFP_KERNEL);
if (!fc_req)
return;
ret = zfcp_fc_wka_port_get(&adapter->gs->ds);
if (ret)
goto out_free;
ret = zfcp_fc_gspn(adapter, fc_req);
if (ret || fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV)
goto out_ds_put;
memset(fc_req, 0, sizeof(*fc_req));
zfcp_fc_rspn(adapter, fc_req);
out_ds_put:
zfcp_fc_wka_port_put(&adapter->gs->ds);
out_free:
kmem_cache_free(zfcp_fc_req_cache, fc_req);
}
static void zfcp_fc_ct_els_job_handler(void *data)
{
struct fc_bsg_job *job = data;

View file

@ -64,32 +64,15 @@ struct zfcp_fc_gid_pn_req {
} __packed;
/**
* struct zfcp_fc_gid_pn_resp - container for ct header plus gid_pn response
* struct zfcp_fc_gid_pn_rsp - container for ct header plus gid_pn response
* @ct_hdr: FC GS common transport header
* @gid_pn: GID_PN response
*/
struct zfcp_fc_gid_pn_resp {
struct zfcp_fc_gid_pn_rsp {
struct fc_ct_hdr ct_hdr;
struct fc_gid_pn_resp gid_pn;
} __packed;
/**
* struct zfcp_fc_gid_pn - everything required in zfcp for gid_pn request
* @ct: data passed to zfcp_fsf for issuing fsf request
* @sg_req: scatterlist entry for request data
* @sg_resp: scatterlist entry for response data
* @gid_pn_req: GID_PN request data
* @gid_pn_resp: GID_PN response data
*/
struct zfcp_fc_gid_pn {
struct zfcp_fsf_ct_els ct;
struct scatterlist sg_req;
struct scatterlist sg_resp;
struct zfcp_fc_gid_pn_req gid_pn_req;
struct zfcp_fc_gid_pn_resp gid_pn_resp;
struct zfcp_port *port;
};
/**
* struct zfcp_fc_gpn_ft - container for ct header plus gpn_ft request
* @ct_hdr: FC GS common transport header
@ -101,41 +84,72 @@ struct zfcp_fc_gpn_ft_req {
} __packed;
/**
* struct zfcp_fc_gpn_ft_resp - container for ct header plus gpn_ft response
* struct zfcp_fc_gspn_req - container for ct header plus GSPN_ID request
* @ct_hdr: FC GS common transport header
* @gpn_ft: Array of gpn_ft response data to fill one memory page
* @gspn: GSPN_ID request
*/
struct zfcp_fc_gpn_ft_resp {
struct zfcp_fc_gspn_req {
struct fc_ct_hdr ct_hdr;
struct fc_gpn_ft_resp gpn_ft[ZFCP_FC_GPN_FT_ENT_PAGE];
struct fc_gid_pn_resp gspn;
} __packed;
/**
* struct zfcp_fc_gpn_ft - zfcp data for gpn_ft request
* @ct: data passed to zfcp_fsf for issuing fsf request
* @sg_req: scatter list entry for gpn_ft request
* @sg_resp: scatter list entries for gpn_ft responses (per memory page)
* struct zfcp_fc_gspn_rsp - container for ct header plus GSPN_ID response
* @ct_hdr: FC GS common transport header
* @gspn: GSPN_ID response
* @name: The name string of the GSPN_ID response
*/
struct zfcp_fc_gpn_ft {
struct zfcp_fsf_ct_els ct;
struct scatterlist sg_req;
struct scatterlist sg_resp[ZFCP_FC_GPN_FT_NUM_BUFS];
};
struct zfcp_fc_gspn_rsp {
struct fc_ct_hdr ct_hdr;
struct fc_gspn_resp gspn;
char name[FC_SYMBOLIC_NAME_SIZE];
} __packed;
/**
* struct zfcp_fc_els_adisc - everything required in zfcp for issuing ELS ADISC
* @els: data required for issuing els fsf command
* @req: scatterlist entry for ELS ADISC request
* @resp: scatterlist entry for ELS ADISC response
* @adisc_req: ELS ADISC request data
* @adisc_resp: ELS ADISC response data
* struct zfcp_fc_rspn_req - container for ct header plus RSPN_ID request
* @ct_hdr: FC GS common transport header
* @rspn: RSPN_ID request
* @name: The name string of the RSPN_ID request
*/
struct zfcp_fc_els_adisc {
struct zfcp_fsf_ct_els els;
struct scatterlist req;
struct scatterlist resp;
struct fc_els_adisc adisc_req;
struct fc_els_adisc adisc_resp;
struct zfcp_fc_rspn_req {
struct fc_ct_hdr ct_hdr;
struct fc_ns_rspn rspn;
char name[FC_SYMBOLIC_NAME_SIZE];
} __packed;
/**
* struct zfcp_fc_req - Container for FC ELS and CT requests sent from zfcp
* @ct_els: data required for issuing fsf command
* @sg_req: scatterlist entry for request data
* @sg_rsp: scatterlist entry for response data
* @u: request specific data
*/
struct zfcp_fc_req {
struct zfcp_fsf_ct_els ct_els;
struct scatterlist sg_req;
struct scatterlist sg_rsp;
union {
struct {
struct fc_els_adisc req;
struct fc_els_adisc rsp;
} adisc;
struct {
struct zfcp_fc_gid_pn_req req;
struct zfcp_fc_gid_pn_rsp rsp;
} gid_pn;
struct {
struct scatterlist sg_rsp2[ZFCP_FC_GPN_FT_NUM_BUFS - 1];
struct zfcp_fc_gpn_ft_req req;
} gpn_ft;
struct {
struct zfcp_fc_gspn_req req;
struct zfcp_fc_gspn_rsp rsp;
} gspn;
struct {
struct zfcp_fc_rspn_req req;
struct fc_ct_hdr rsp;
} rspn;
} u;
};
/**
@ -192,14 +206,21 @@ struct zfcp_fc_wka_ports {
* zfcp_fc_scsi_to_fcp - setup FCP command with data from scsi_cmnd
* @fcp: fcp_cmnd to setup
* @scsi: scsi_cmnd where to get LUN, task attributes/flags and CDB
* @tm: task management flags to setup task management command
*/
static inline
void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi)
void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi,
u8 tm_flags)
{
char tag[2];
int_to_scsilun(scsi->device->lun, (struct scsi_lun *) &fcp->fc_lun);
if (unlikely(tm_flags)) {
fcp->fc_tm_flags = tm_flags;
return;
}
if (scsi_populate_tag_msg(scsi, tag)) {
switch (tag[0]) {
case MSG_ORDERED_TAG:
@ -225,19 +246,6 @@ void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi)
fcp->fc_dl += fcp->fc_dl / scsi->device->sector_size * 8;
}
/**
* zfcp_fc_fcp_tm - setup FCP command as task management command
* @fcp: fcp_cmnd to setup
* @dev: scsi_device where to send the task management command
* @tm: task management flags to setup tm command
*/
static inline
void zfcp_fc_fcp_tm(struct fcp_cmnd *fcp, struct scsi_device *dev, u8 tm_flags)
{
int_to_scsilun(dev->lun, (struct scsi_lun *) &fcp->fc_lun);
fcp->fc_tm_flags |= tm_flags;
}
/**
* zfcp_fc_evap_fcp_rsp - evaluate FCP RSP IU and update scsi_cmnd accordingly
* @fcp_rsp: FCP RSP IU to evaluate

View file

@ -18,6 +18,8 @@
#include "zfcp_qdio.h"
#include "zfcp_reqlist.h"
struct kmem_cache *zfcp_fsf_qtcb_cache;
static void zfcp_fsf_request_timeout_handler(unsigned long data)
{
struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
@ -83,7 +85,7 @@ void zfcp_fsf_req_free(struct zfcp_fsf_req *req)
}
if (likely(req->qtcb))
kmem_cache_free(zfcp_data.qtcb_cache, req->qtcb);
kmem_cache_free(zfcp_fsf_qtcb_cache, req->qtcb);
kfree(req);
}
@ -212,7 +214,7 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
zfcp_dbf_hba_fsf_uss("fssrh_1", req);
mempool_free(sr_buf, adapter->pool.status_read_data);
mempool_free(virt_to_page(sr_buf), adapter->pool.sr_data);
zfcp_fsf_req_free(req);
return;
}
@ -265,7 +267,7 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
break;
}
mempool_free(sr_buf, adapter->pool.status_read_data);
mempool_free(virt_to_page(sr_buf), adapter->pool.sr_data);
zfcp_fsf_req_free(req);
atomic_inc(&adapter->stat_miss);
@ -628,7 +630,7 @@ static struct fsf_qtcb *zfcp_qtcb_alloc(mempool_t *pool)
if (likely(pool))
qtcb = mempool_alloc(pool, GFP_ATOMIC);
else
qtcb = kmem_cache_alloc(zfcp_data.qtcb_cache, GFP_ATOMIC);
qtcb = kmem_cache_alloc(zfcp_fsf_qtcb_cache, GFP_ATOMIC);
if (unlikely(!qtcb))
return NULL;
@ -723,6 +725,7 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio)
struct zfcp_adapter *adapter = qdio->adapter;
struct zfcp_fsf_req *req;
struct fsf_status_read_buffer *sr_buf;
struct page *page;
int retval = -EIO;
spin_lock_irq(&qdio->req_q_lock);
@ -736,11 +739,12 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio)
goto out;
}
sr_buf = mempool_alloc(adapter->pool.status_read_data, GFP_ATOMIC);
if (!sr_buf) {
page = mempool_alloc(adapter->pool.sr_data, GFP_ATOMIC);
if (!page) {
retval = -ENOMEM;
goto failed_buf;
}
sr_buf = page_address(page);
memset(sr_buf, 0, sizeof(*sr_buf));
req->data = sr_buf;
@ -755,7 +759,7 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio)
failed_req_send:
req->data = NULL;
mempool_free(sr_buf, adapter->pool.status_read_data);
mempool_free(virt_to_page(sr_buf), adapter->pool.sr_data);
failed_buf:
zfcp_dbf_hba_fsf_uss("fssr__1", req);
zfcp_fsf_req_free(req);
@ -1552,7 +1556,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
SBAL_FLAGS0_TYPE_READ,
qdio->adapter->pool.erp_req);
if (unlikely(IS_ERR(req))) {
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
@ -1605,7 +1609,7 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
SBAL_FLAGS0_TYPE_READ,
qdio->adapter->pool.erp_req);
if (unlikely(IS_ERR(req))) {
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
@ -2206,7 +2210,7 @@ int zfcp_fsf_fcp_cmnd(struct scsi_cmnd *scsi_cmnd)
zfcp_fsf_set_data_dir(scsi_cmnd, &io->data_direction);
fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd);
zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd, 0);
if (scsi_prot_sg_count(scsi_cmnd)) {
zfcp_qdio_set_data_div(qdio, &req->qdio_req,
@ -2284,7 +2288,6 @@ struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd,
goto out;
}
req->status |= ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT;
req->data = scmnd;
req->handler = zfcp_fsf_fcp_task_mgmt_handler;
req->qtcb->header.lun_handle = zfcp_sdev->lun_handle;
@ -2296,7 +2299,7 @@ struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd,
zfcp_qdio_set_sbale_last(qdio, &req->qdio_req);
fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
zfcp_fc_fcp_tm(fcp_cmnd, scmnd->device, tm_flags);
zfcp_fc_scsi_to_fcp(fcp_cmnd, scmnd, tm_flags);
zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT);
if (!zfcp_fsf_req_send(req))

View file

@ -292,7 +292,37 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
return SUCCESS;
}
int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
struct scsi_transport_template *zfcp_scsi_transport_template;
static struct scsi_host_template zfcp_scsi_host_template = {
.module = THIS_MODULE,
.name = "zfcp",
.queuecommand = zfcp_scsi_queuecommand,
.eh_abort_handler = zfcp_scsi_eh_abort_handler,
.eh_device_reset_handler = zfcp_scsi_eh_device_reset_handler,
.eh_target_reset_handler = zfcp_scsi_eh_target_reset_handler,
.eh_host_reset_handler = zfcp_scsi_eh_host_reset_handler,
.slave_alloc = zfcp_scsi_slave_alloc,
.slave_configure = zfcp_scsi_slave_configure,
.slave_destroy = zfcp_scsi_slave_destroy,
.change_queue_depth = zfcp_scsi_change_queue_depth,
.proc_name = "zfcp",
.can_queue = 4096,
.this_id = -1,
.sg_tablesize = ZFCP_QDIO_MAX_SBALES_PER_REQ,
.max_sectors = (ZFCP_QDIO_MAX_SBALES_PER_REQ * 8),
.dma_boundary = ZFCP_QDIO_SBALE_LEN - 1,
.cmd_per_lun = 1,
.use_clustering = 1,
.shost_attrs = zfcp_sysfs_shost_attrs,
.sdev_attrs = zfcp_sysfs_sdev_attrs,
};
/**
* zfcp_scsi_adapter_register - Register SCSI and FC host with SCSI midlayer
* @adapter: The zfcp adapter to register with the SCSI midlayer
*/
int zfcp_scsi_adapter_register(struct zfcp_adapter *adapter)
{
struct ccw_dev_id dev_id;
@ -301,7 +331,7 @@ int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
ccw_device_get_id(adapter->ccw_device, &dev_id);
/* register adapter as SCSI host with mid layer of SCSI stack */
adapter->scsi_host = scsi_host_alloc(&zfcp_data.scsi_host_template,
adapter->scsi_host = scsi_host_alloc(&zfcp_scsi_host_template,
sizeof (struct zfcp_adapter *));
if (!adapter->scsi_host) {
dev_err(&adapter->ccw_device->dev,
@ -316,7 +346,7 @@ int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
adapter->scsi_host->max_channel = 0;
adapter->scsi_host->unique_id = dev_id.devno;
adapter->scsi_host->max_cmd_len = 16; /* in struct fcp_cmnd */
adapter->scsi_host->transportt = zfcp_data.scsi_transport_template;
adapter->scsi_host->transportt = zfcp_scsi_transport_template;
adapter->scsi_host->hostdata[0] = (unsigned long) adapter;
@ -328,7 +358,11 @@ int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
return 0;
}
void zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
/**
* zfcp_scsi_adapter_unregister - Unregister SCSI and FC host from SCSI midlayer
* @adapter: The zfcp adapter to unregister.
*/
void zfcp_scsi_adapter_unregister(struct zfcp_adapter *adapter)
{
struct Scsi_Host *shost;
struct zfcp_port *port;
@ -346,8 +380,6 @@ void zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
scsi_remove_host(shost);
scsi_host_put(shost);
adapter->scsi_host = NULL;
return;
}
static struct fc_host_statistics*
@ -688,33 +720,8 @@ struct fc_function_template zfcp_transport_functions = {
/* no functions registered for following dynamic attributes but
directly set by LLDD */
.show_host_port_type = 1,
.show_host_symbolic_name = 1,
.show_host_speed = 1,
.show_host_port_id = 1,
.dd_bsg_size = sizeof(struct zfcp_fsf_ct_els),
};
struct zfcp_data zfcp_data = {
.scsi_host_template = {
.name = "zfcp",
.module = THIS_MODULE,
.proc_name = "zfcp",
.change_queue_depth = zfcp_scsi_change_queue_depth,
.slave_alloc = zfcp_scsi_slave_alloc,
.slave_configure = zfcp_scsi_slave_configure,
.slave_destroy = zfcp_scsi_slave_destroy,
.queuecommand = zfcp_scsi_queuecommand,
.eh_abort_handler = zfcp_scsi_eh_abort_handler,
.eh_device_reset_handler = zfcp_scsi_eh_device_reset_handler,
.eh_target_reset_handler = zfcp_scsi_eh_target_reset_handler,
.eh_host_reset_handler = zfcp_scsi_eh_host_reset_handler,
.can_queue = 4096,
.this_id = -1,
.sg_tablesize = ZFCP_QDIO_MAX_SBALES_PER_REQ,
.cmd_per_lun = 1,
.use_clustering = 1,
.sdev_attrs = zfcp_sysfs_sdev_attrs,
.max_sectors = (ZFCP_QDIO_MAX_SBALES_PER_REQ * 8),
.dma_boundary = ZFCP_QDIO_SBALE_LEN - 1,
.shost_attrs = zfcp_sysfs_shost_attrs,
},
};

View file

@ -381,6 +381,7 @@ config ISCSI_BOOT_SYSFS
source "drivers/scsi/cxgbi/Kconfig"
source "drivers/scsi/bnx2i/Kconfig"
source "drivers/scsi/bnx2fc/Kconfig"
source "drivers/scsi/be2iscsi/Kconfig"
config SGIWD93_SCSI

View file

@ -40,6 +40,7 @@ obj-$(CONFIG_LIBFC) += libfc/
obj-$(CONFIG_LIBFCOE) += fcoe/
obj-$(CONFIG_FCOE) += fcoe/
obj-$(CONFIG_FCOE_FNIC) += fnic/
obj-$(CONFIG_SCSI_BNX2X_FCOE) += libfc/ fcoe/ bnx2fc/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o

View file

@ -936,8 +936,7 @@ static void NCR5380_exit(struct Scsi_Host *instance)
{
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
cancel_delayed_work(&hostdata->coroutine);
flush_scheduled_work();
cancel_delayed_work_sync(&hostdata->coroutine);
}
/**

View file

@ -1020,7 +1020,7 @@ static void arcmsr_remove(struct pci_dev *pdev)
int poll_count = 0;
arcmsr_free_sysfs_attr(acb);
scsi_remove_host(host);
flush_scheduled_work();
flush_work_sync(&acb->arcmsr_do_message_isr_bh);
del_timer_sync(&acb->eternal_timer);
arcmsr_disable_outbound_ints(acb);
arcmsr_stop_adapter_bgrb(acb);
@ -1066,7 +1066,7 @@ static void arcmsr_shutdown(struct pci_dev *pdev)
(struct AdapterControlBlock *)host->hostdata;
del_timer_sync(&acb->eternal_timer);
arcmsr_disable_outbound_ints(acb);
flush_scheduled_work();
flush_work_sync(&acb->arcmsr_do_message_isr_bh);
arcmsr_stop_adapter_bgrb(acb);
arcmsr_flush_adapter_cache(acb);
}

View file

@ -210,28 +210,20 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
}
/**
* beiscsi_conn_get_param - get the iscsi parameter
* @cls_conn: pointer to iscsi cls conn
* beiscsi_ep_get_param - get the iscsi parameter
* @ep: pointer to iscsi ep
* @param: parameter type identifier
* @buf: buffer pointer
*
* returns iscsi parameter
*/
int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
int beiscsi_ep_get_param(struct iscsi_endpoint *ep,
enum iscsi_param param, char *buf)
{
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_conn *conn = cls_conn->dd_data;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
int len = 0;
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_param, param= %d\n", param);
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep) {
SE_DEBUG(DBG_LVL_1,
"In beiscsi_conn_get_param , no beiscsi_ep\n");
return -ENODEV;
}
switch (param) {
case ISCSI_PARAM_CONN_PORT:
@ -244,7 +236,7 @@ int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr);
break;
default:
return iscsi_conn_get_param(cls_conn, param, buf);
return -ENOSYS;
}
return len;
}

View file

@ -48,8 +48,8 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn,
uint64_t transport_fd, int is_leading);
int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf);
int beiscsi_ep_get_param(struct iscsi_endpoint *ep, enum iscsi_param param,
char *buf);
int beiscsi_get_host_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf);

View file

@ -4384,7 +4384,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
.bind_conn = beiscsi_conn_bind,
.destroy_conn = iscsi_conn_teardown,
.set_param = beiscsi_set_param,
.get_conn_param = beiscsi_conn_get_param,
.get_conn_param = iscsi_conn_get_param,
.get_session_param = iscsi_session_get_param,
.get_host_param = beiscsi_get_host_param,
.start_conn = beiscsi_conn_start,
@ -4395,6 +4395,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
.alloc_pdu = beiscsi_alloc_pdu,
.parse_pdu_itt = beiscsi_parse_pdu,
.get_stats = beiscsi_conn_get_stats,
.get_ep_param = beiscsi_ep_get_param,
.ep_connect = beiscsi_ep_connect,
.ep_poll = beiscsi_ep_poll,
.ep_disconnect = beiscsi_ep_disconnect,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
config SCSI_BNX2X_FCOE
tristate "Broadcom NetXtreme II FCoE support"
depends on PCI
select NETDEVICES
select NETDEV_1000
select LIBFC
select LIBFCOE
select CNIC
---help---
This driver supports FCoE offload for the Broadcom NetXtreme II
devices.

View file

@ -0,0 +1,3 @@
obj-$(CONFIG_SCSI_BNX2X_FCOE) += bnx2fc.o
bnx2fc-y := bnx2fc_els.o bnx2fc_fcoe.o bnx2fc_hwi.o bnx2fc_io.o bnx2fc_tgt.o

View file

@ -0,0 +1,511 @@
#ifndef _BNX2FC_H_
#define _BNX2FC_H_
/* bnx2fc.h: Broadcom NetXtreme II Linux FCoE offload driver.
*
* Copyright (c) 2008 - 2010 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: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/kthread.h>
#include <linux/crc32.h>
#include <linux/cpu.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
#include <linux/log2.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/io.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_tcq.h>
#include <scsi/libfc.h>
#include <scsi/libfcoe.h>
#include <scsi/fc_encode.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fip.h>
#include <scsi/fc/fc_fc2.h>
#include <scsi/fc_frame.h>
#include <scsi/fc/fc_fcoe.h>
#include <scsi/fc/fc_fcp.h>
#include "57xx_hsi_bnx2fc.h"
#include "bnx2fc_debug.h"
#include "../../net/cnic_if.h"
#include "bnx2fc_constants.h"
#define BNX2FC_NAME "bnx2fc"
#define BNX2FC_VERSION "1.0.0"
#define PFX "bnx2fc: "
#define BNX2X_DOORBELL_PCI_BAR 2
#define BNX2FC_MAX_BD_LEN 0xffff
#define BNX2FC_BD_SPLIT_SZ 0x8000
#define BNX2FC_MAX_BDS_PER_CMD 256
#define BNX2FC_SQ_WQES_MAX 256
#define BNX2FC_SCSI_MAX_SQES ((3 * BNX2FC_SQ_WQES_MAX) / 8)
#define BNX2FC_TM_MAX_SQES ((BNX2FC_SQ_WQES_MAX) / 2)
#define BNX2FC_ELS_MAX_SQES (BNX2FC_TM_MAX_SQES - 1)
#define BNX2FC_RQ_WQES_MAX 16
#define BNX2FC_CQ_WQES_MAX (BNX2FC_SQ_WQES_MAX + BNX2FC_RQ_WQES_MAX)
#define BNX2FC_NUM_MAX_SESS 128
#define BNX2FC_NUM_MAX_SESS_LOG (ilog2(BNX2FC_NUM_MAX_SESS))
#define BNX2FC_MAX_OUTSTANDING_CMNDS 4096
#define BNX2FC_MIN_PAYLOAD 256
#define BNX2FC_MAX_PAYLOAD 2048
#define BNX2FC_RQ_BUF_SZ 256
#define BNX2FC_RQ_BUF_LOG_SZ (ilog2(BNX2FC_RQ_BUF_SZ))
#define BNX2FC_SQ_WQE_SIZE (sizeof(struct fcoe_sqe))
#define BNX2FC_CQ_WQE_SIZE (sizeof(struct fcoe_cqe))
#define BNX2FC_RQ_WQE_SIZE (BNX2FC_RQ_BUF_SZ)
#define BNX2FC_XFERQ_WQE_SIZE (sizeof(struct fcoe_xfrqe))
#define BNX2FC_CONFQ_WQE_SIZE (sizeof(struct fcoe_confqe))
#define BNX2FC_5771X_DB_PAGE_SIZE 128
#define BNX2FC_MAX_TASKS BNX2FC_MAX_OUTSTANDING_CMNDS
#define BNX2FC_TASK_SIZE 128
#define BNX2FC_TASKS_PER_PAGE (PAGE_SIZE/BNX2FC_TASK_SIZE)
#define BNX2FC_TASK_CTX_ARR_SZ (BNX2FC_MAX_TASKS/BNX2FC_TASKS_PER_PAGE)
#define BNX2FC_MAX_ROWS_IN_HASH_TBL 8
#define BNX2FC_HASH_TBL_CHUNK_SIZE (16 * 1024)
#define BNX2FC_MAX_SEQS 255
#define BNX2FC_READ (1 << 1)
#define BNX2FC_WRITE (1 << 0)
#define BNX2FC_MIN_XID 0
#define BNX2FC_MAX_XID (BNX2FC_MAX_OUTSTANDING_CMNDS - 1)
#define FCOE_MIN_XID (BNX2FC_MAX_OUTSTANDING_CMNDS)
#define FCOE_MAX_XID \
(BNX2FC_MAX_OUTSTANDING_CMNDS + (nr_cpu_ids * 256))
#define BNX2FC_MAX_LUN 0xFFFF
#define BNX2FC_MAX_FCP_TGT 256
#define BNX2FC_MAX_CMD_LEN 16
#define BNX2FC_TM_TIMEOUT 60 /* secs */
#define BNX2FC_IO_TIMEOUT 20000UL /* msecs */
#define BNX2FC_WAIT_CNT 120
#define BNX2FC_FW_TIMEOUT (3 * HZ)
#define PORT_MAX 2
#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
/* FC FCP Status */
#define FC_GOOD 0
#define BNX2FC_RNID_HBA 0x7
/* bnx2fc driver uses only one instance of fcoe_percpu_s */
extern struct fcoe_percpu_s bnx2fc_global;
extern struct workqueue_struct *bnx2fc_wq;
struct bnx2fc_percpu_s {
struct task_struct *iothread;
struct list_head work_list;
spinlock_t fp_work_lock;
};
struct bnx2fc_hba {
struct list_head link;
struct cnic_dev *cnic;
struct pci_dev *pcidev;
struct net_device *netdev;
struct net_device *phys_dev;
unsigned long reg_with_cnic;
#define BNX2FC_CNIC_REGISTERED 1
struct packet_type fcoe_packet_type;
struct packet_type fip_packet_type;
struct bnx2fc_cmd_mgr *cmd_mgr;
struct workqueue_struct *timer_work_queue;
struct kref kref;
spinlock_t hba_lock;
struct mutex hba_mutex;
unsigned long adapter_state;
#define ADAPTER_STATE_UP 0
#define ADAPTER_STATE_GOING_DOWN 1
#define ADAPTER_STATE_LINK_DOWN 2
#define ADAPTER_STATE_READY 3
u32 flags;
unsigned long init_done;
#define BNX2FC_FW_INIT_DONE 0
#define BNX2FC_CTLR_INIT_DONE 1
#define BNX2FC_CREATE_DONE 2
struct fcoe_ctlr ctlr;
u8 vlan_enabled;
int vlan_id;
u32 next_conn_id;
struct fcoe_task_ctx_entry **task_ctx;
dma_addr_t *task_ctx_dma;
struct regpair *task_ctx_bd_tbl;
dma_addr_t task_ctx_bd_dma;
int hash_tbl_segment_count;
void **hash_tbl_segments;
void *hash_tbl_pbl;
dma_addr_t hash_tbl_pbl_dma;
struct fcoe_t2_hash_table_entry *t2_hash_tbl;
dma_addr_t t2_hash_tbl_dma;
char *t2_hash_tbl_ptr;
dma_addr_t t2_hash_tbl_ptr_dma;
char *dummy_buffer;
dma_addr_t dummy_buf_dma;
struct fcoe_statistics_params *stats_buffer;
dma_addr_t stats_buf_dma;
/*
* PCI related info.
*/
u16 pci_did;
u16 pci_vid;
u16 pci_sdid;
u16 pci_svid;
u16 pci_func;
u16 pci_devno;
struct task_struct *l2_thread;
/* linkdown handling */
wait_queue_head_t shutdown_wait;
int wait_for_link_down;
/*destroy handling */
struct timer_list destroy_timer;
wait_queue_head_t destroy_wait;
/* Active list of offloaded sessions */
struct bnx2fc_rport *tgt_ofld_list[BNX2FC_NUM_MAX_SESS];
int num_ofld_sess;
/* statistics */
struct completion stat_req_done;
};
#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_hba, ctlr)
struct bnx2fc_cmd_mgr {
struct bnx2fc_hba *hba;
u16 next_idx;
struct list_head *free_list;
spinlock_t *free_list_lock;
struct io_bdt **io_bdt_pool;
struct bnx2fc_cmd **cmds;
};
struct bnx2fc_rport {
struct fcoe_port *port;
struct fc_rport *rport;
struct fc_rport_priv *rdata;
void __iomem *ctx_base;
#define DPM_TRIGER_TYPE 0x40
u32 fcoe_conn_id;
u32 context_id;
u32 sid;
unsigned long flags;
#define BNX2FC_FLAG_SESSION_READY 0x1
#define BNX2FC_FLAG_OFFLOADED 0x2
#define BNX2FC_FLAG_DISABLED 0x3
#define BNX2FC_FLAG_DESTROYED 0x4
#define BNX2FC_FLAG_OFLD_REQ_CMPL 0x5
#define BNX2FC_FLAG_DESTROY_CMPL 0x6
#define BNX2FC_FLAG_CTX_ALLOC_FAILURE 0x7
#define BNX2FC_FLAG_UPLD_REQ_COMPL 0x8
#define BNX2FC_FLAG_EXPL_LOGO 0x9
u32 max_sqes;
u32 max_rqes;
u32 max_cqes;
struct fcoe_sqe *sq;
dma_addr_t sq_dma;
u16 sq_prod_idx;
u8 sq_curr_toggle_bit;
u32 sq_mem_size;
struct fcoe_cqe *cq;
dma_addr_t cq_dma;
u32 cq_cons_idx;
u8 cq_curr_toggle_bit;
u32 cq_mem_size;
void *rq;
dma_addr_t rq_dma;
u32 rq_prod_idx;
u32 rq_cons_idx;
u32 rq_mem_size;
void *rq_pbl;
dma_addr_t rq_pbl_dma;
u32 rq_pbl_size;
struct fcoe_xfrqe *xferq;
dma_addr_t xferq_dma;
u32 xferq_mem_size;
struct fcoe_confqe *confq;
dma_addr_t confq_dma;
u32 confq_mem_size;
void *confq_pbl;
dma_addr_t confq_pbl_dma;
u32 confq_pbl_size;
struct fcoe_conn_db *conn_db;
dma_addr_t conn_db_dma;
u32 conn_db_mem_size;
struct fcoe_sqe *lcq;
dma_addr_t lcq_dma;
u32 lcq_mem_size;
void *ofld_req[4];
dma_addr_t ofld_req_dma[4];
void *enbl_req;
dma_addr_t enbl_req_dma;
spinlock_t tgt_lock;
spinlock_t cq_lock;
atomic_t num_active_ios;
u32 flush_in_prog;
unsigned long work_time_slice;
unsigned long timestamp;
struct list_head free_task_list;
struct bnx2fc_cmd *pending_queue[BNX2FC_SQ_WQES_MAX+1];
atomic_t pi;
atomic_t ci;
struct list_head active_cmd_queue;
struct list_head els_queue;
struct list_head io_retire_queue;
struct list_head active_tm_queue;
struct timer_list ofld_timer;
wait_queue_head_t ofld_wait;
struct timer_list upld_timer;
wait_queue_head_t upld_wait;
};
struct bnx2fc_mp_req {
u8 tm_flags;
u32 req_len;
void *req_buf;
dma_addr_t req_buf_dma;
struct fcoe_bd_ctx *mp_req_bd;
dma_addr_t mp_req_bd_dma;
struct fc_frame_header req_fc_hdr;
u32 resp_len;
void *resp_buf;
dma_addr_t resp_buf_dma;
struct fcoe_bd_ctx *mp_resp_bd;
dma_addr_t mp_resp_bd_dma;
struct fc_frame_header resp_fc_hdr;
};
struct bnx2fc_els_cb_arg {
struct bnx2fc_cmd *aborted_io_req;
struct bnx2fc_cmd *io_req;
u16 l2_oxid;
};
/* bnx2fc command structure */
struct bnx2fc_cmd {
struct list_head link;
u8 on_active_queue;
u8 on_tmf_queue;
u8 cmd_type;
#define BNX2FC_SCSI_CMD 1
#define BNX2FC_TASK_MGMT_CMD 2
#define BNX2FC_ABTS 3
#define BNX2FC_ELS 4
#define BNX2FC_CLEANUP 5
u8 io_req_flags;
struct kref refcount;
struct fcoe_port *port;
struct bnx2fc_rport *tgt;
struct scsi_cmnd *sc_cmd;
struct bnx2fc_cmd_mgr *cmd_mgr;
struct bnx2fc_mp_req mp_req;
void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg);
struct bnx2fc_els_cb_arg *cb_arg;
struct delayed_work timeout_work; /* timer for ULP timeouts */
struct completion tm_done;
int wait_for_comp;
u16 xid;
struct fcoe_task_ctx_entry *task;
struct io_bdt *bd_tbl;
struct fcp_rsp *rsp;
size_t data_xfer_len;
unsigned long req_flags;
#define BNX2FC_FLAG_ISSUE_RRQ 0x1
#define BNX2FC_FLAG_ISSUE_ABTS 0x2
#define BNX2FC_FLAG_ABTS_DONE 0x3
#define BNX2FC_FLAG_TM_COMPL 0x4
#define BNX2FC_FLAG_TM_TIMEOUT 0x5
#define BNX2FC_FLAG_IO_CLEANUP 0x6
#define BNX2FC_FLAG_RETIRE_OXID 0x7
#define BNX2FC_FLAG_EH_ABORT 0x8
#define BNX2FC_FLAG_IO_COMPL 0x9
#define BNX2FC_FLAG_ELS_DONE 0xa
#define BNX2FC_FLAG_ELS_TIMEOUT 0xb
u32 fcp_resid;
u32 fcp_rsp_len;
u32 fcp_sns_len;
u8 cdb_status; /* SCSI IO status */
u8 fcp_status; /* FCP IO status */
u8 fcp_rsp_code;
u8 scsi_comp_flags;
};
struct io_bdt {
struct bnx2fc_cmd *io_req;
struct fcoe_bd_ctx *bd_tbl;
dma_addr_t bd_tbl_dma;
u16 bd_valid;
};
struct bnx2fc_work {
struct list_head list;
struct bnx2fc_rport *tgt;
u16 wqe;
};
struct bnx2fc_unsol_els {
struct fc_lport *lport;
struct fc_frame *fp;
struct work_struct unsol_els_work;
};
struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type);
void bnx2fc_cmd_release(struct kref *ref);
int bnx2fc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd);
int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba);
int bnx2fc_send_fw_fcoe_destroy_msg(struct bnx2fc_hba *hba);
int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
struct bnx2fc_rport *tgt);
int bnx2fc_send_session_disable_req(struct fcoe_port *port,
struct bnx2fc_rport *tgt);
int bnx2fc_send_session_destroy_req(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
int bnx2fc_map_doorbell(struct bnx2fc_rport *tgt);
void bnx2fc_indicate_kcqe(void *context, struct kcqe *kcq[],
u32 num_cqe);
int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba);
void bnx2fc_free_task_ctx(struct bnx2fc_hba *hba);
int bnx2fc_setup_fw_resc(struct bnx2fc_hba *hba);
void bnx2fc_free_fw_resc(struct bnx2fc_hba *hba);
struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba,
u16 min_xid, u16 max_xid);
void bnx2fc_cmd_mgr_free(struct bnx2fc_cmd_mgr *cmgr);
void bnx2fc_get_link_state(struct bnx2fc_hba *hba);
char *bnx2fc_get_next_rqe(struct bnx2fc_rport *tgt, u8 num_items);
void bnx2fc_return_rqe(struct bnx2fc_rport *tgt, u8 num_items);
int bnx2fc_get_paged_crc_eof(struct sk_buff *skb, int tlen);
int bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req);
int bnx2fc_send_adisc(struct bnx2fc_rport *tgt, struct fc_frame *fp);
int bnx2fc_send_logo(struct bnx2fc_rport *tgt, struct fc_frame *fp);
int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp);
int bnx2fc_initiate_cleanup(struct bnx2fc_cmd *io_req);
int bnx2fc_initiate_abts(struct bnx2fc_cmd *io_req);
void bnx2fc_cmd_timer_set(struct bnx2fc_cmd *io_req,
unsigned int timer_msec);
int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req);
void bnx2fc_init_cleanup_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u16 orig_xid);
void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task);
void bnx2fc_init_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task);
void bnx2fc_add_2_sq(struct bnx2fc_rport *tgt, u16 xid);
void bnx2fc_ring_doorbell(struct bnx2fc_rport *tgt);
int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd);
int bnx2fc_eh_host_reset(struct scsi_cmnd *sc_cmd);
int bnx2fc_eh_target_reset(struct scsi_cmnd *sc_cmd);
int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd);
void bnx2fc_rport_event_handler(struct fc_lport *lport,
struct fc_rport_priv *rport,
enum fc_rport_event event);
void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_process_tm_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_build_fcp_cmnd(struct bnx2fc_cmd *io_req,
struct fcp_cmnd *fcp_cmnd);
void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt);
struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did,
struct fc_frame *fp, unsigned int op,
void (*resp)(struct fc_seq *,
struct fc_frame *,
void *),
void *arg, u32 timeout);
int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt);
void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe);
struct bnx2fc_rport *bnx2fc_tgt_lookup(struct fcoe_port *port,
u32 port_id);
void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
unsigned char *buf,
u32 frame_len, u16 l2_oxid);
int bnx2fc_send_stat_req(struct bnx2fc_hba *hba);
#endif

View file

@ -0,0 +1,206 @@
#ifndef __BNX2FC_CONSTANTS_H_
#define __BNX2FC_CONSTANTS_H_
/**
* This file defines HSI constants for the FCoE flows
*/
/* KWQ/KCQ FCoE layer code */
#define FCOE_KWQE_LAYER_CODE (7)
/* KWQ (kernel work queue) request op codes */
#define FCOE_KWQE_OPCODE_INIT1 (0)
#define FCOE_KWQE_OPCODE_INIT2 (1)
#define FCOE_KWQE_OPCODE_INIT3 (2)
#define FCOE_KWQE_OPCODE_OFFLOAD_CONN1 (3)
#define FCOE_KWQE_OPCODE_OFFLOAD_CONN2 (4)
#define FCOE_KWQE_OPCODE_OFFLOAD_CONN3 (5)
#define FCOE_KWQE_OPCODE_OFFLOAD_CONN4 (6)
#define FCOE_KWQE_OPCODE_ENABLE_CONN (7)
#define FCOE_KWQE_OPCODE_DISABLE_CONN (8)
#define FCOE_KWQE_OPCODE_DESTROY_CONN (9)
#define FCOE_KWQE_OPCODE_DESTROY (10)
#define FCOE_KWQE_OPCODE_STAT (11)
/* KCQ (kernel completion queue) response op codes */
#define FCOE_KCQE_OPCODE_INIT_FUNC (0x10)
#define FCOE_KCQE_OPCODE_DESTROY_FUNC (0x11)
#define FCOE_KCQE_OPCODE_STAT_FUNC (0x12)
#define FCOE_KCQE_OPCODE_OFFLOAD_CONN (0x15)
#define FCOE_KCQE_OPCODE_ENABLE_CONN (0x16)
#define FCOE_KCQE_OPCODE_DISABLE_CONN (0x17)
#define FCOE_KCQE_OPCODE_DESTROY_CONN (0x18)
#define FCOE_KCQE_OPCODE_CQ_EVENT_NOTIFICATION (0x20)
#define FCOE_KCQE_OPCODE_FCOE_ERROR (0x21)
/* KCQ (kernel completion queue) completion status */
#define FCOE_KCQE_COMPLETION_STATUS_SUCCESS (0x0)
#define FCOE_KCQE_COMPLETION_STATUS_ERROR (0x1)
#define FCOE_KCQE_COMPLETION_STATUS_INVALID_OPCODE (0x2)
#define FCOE_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE (0x3)
#define FCOE_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE (0x4)
#define FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR (0x5)
/* Unsolicited CQE type */
#define FCOE_UNSOLICITED_FRAME_CQE_TYPE 0
#define FCOE_ERROR_DETECTION_CQE_TYPE 1
#define FCOE_WARNING_DETECTION_CQE_TYPE 2
/* Task context constants */
/* After driver has initialize the task in case timer services required */
#define FCOE_TASK_TX_STATE_INIT 0
/* In case timer services are required then shall be updated by Xstorm after
* start processing the task. In case no timer facilities are required then the
* driver would initialize the state to this value */
#define FCOE_TASK_TX_STATE_NORMAL 1
/* Task is under abort procedure. Updated in order to stop processing of
* pending WQEs on this task */
#define FCOE_TASK_TX_STATE_ABORT 2
/* For E_D_T_TOV timer expiration in Xstorm (Class 2 only) */
#define FCOE_TASK_TX_STATE_ERROR 3
/* For REC_TOV timer expiration indication received from Xstorm */
#define FCOE_TASK_TX_STATE_WARNING 4
/* For completed unsolicited task */
#define FCOE_TASK_TX_STATE_UNSOLICITED_COMPLETED 5
/* For exchange cleanup request task */
#define FCOE_TASK_TX_STATE_EXCHANGE_CLEANUP 6
/* For sequence cleanup request task */
#define FCOE_TASK_TX_STATE_SEQUENCE_CLEANUP 7
/* Mark task as aborted and indicate that ABTS was not transmitted */
#define FCOE_TASK_TX_STATE_BEFORE_ABTS_TX 8
/* Mark task as aborted and indicate that ABTS was transmitted */
#define FCOE_TASK_TX_STATE_AFTER_ABTS_TX 9
/* For completion the ABTS task. */
#define FCOE_TASK_TX_STATE_ABTS_TX_COMPLETED 10
/* Mark task as aborted and indicate that Exchange cleanup was not transmitted
*/
#define FCOE_TASK_TX_STATE_BEFORE_EXCHANGE_CLEANUP_TX 11
/* Mark task as aborted and indicate that Exchange cleanup was transmitted */
#define FCOE_TASK_TX_STATE_AFTER_EXCHANGE_CLEANUP_TX 12
#define FCOE_TASK_RX_STATE_NORMAL 0
#define FCOE_TASK_RX_STATE_COMPLETED 1
/* Obsolete: Intermediate completion (middle path with local completion) */
#define FCOE_TASK_RX_STATE_INTER_COMP 2
/* For REC_TOV timer expiration indication received from Xstorm */
#define FCOE_TASK_RX_STATE_WARNING 3
/* For E_D_T_TOV timer expiration in Ustorm */
#define FCOE_TASK_RX_STATE_ERROR 4
/* ABTS ACC arrived wait for local completion to finally complete the task. */
#define FCOE_TASK_RX_STATE_ABTS_ACC_ARRIVED 5
/* local completion arrived wait for ABTS ACC to finally complete the task. */
#define FCOE_TASK_RX_STATE_ABTS_LOCAL_COMP_ARRIVED 6
/* Special completion indication in case of task was aborted. */
#define FCOE_TASK_RX_STATE_ABTS_COMPLETED 7
/* Special completion indication in case of task was cleaned. */
#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_COMPLETED 8
/* Special completion indication (in task requested the exchange cleanup) in
* case cleaned task is in non-valid. */
#define FCOE_TASK_RX_STATE_ABORT_CLEANUP_COMPLETED 9
/* Special completion indication (in task requested the sequence cleanup) in
* case cleaned task was already returned to normal. */
#define FCOE_TASK_RX_STATE_IGNORED_SEQUENCE_CLEANUP 10
/* Exchange cleanup arrived wait until xfer will be handled to finally
* complete the task. */
#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_ARRIVED 11
/* Xfer handled, wait for exchange cleanup to finally complete the task. */
#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_HANDLED_XFER 12
#define FCOE_TASK_TYPE_WRITE 0
#define FCOE_TASK_TYPE_READ 1
#define FCOE_TASK_TYPE_MIDPATH 2
#define FCOE_TASK_TYPE_UNSOLICITED 3
#define FCOE_TASK_TYPE_ABTS 4
#define FCOE_TASK_TYPE_EXCHANGE_CLEANUP 5
#define FCOE_TASK_TYPE_SEQUENCE_CLEANUP 6
#define FCOE_TASK_DEV_TYPE_DISK 0
#define FCOE_TASK_DEV_TYPE_TAPE 1
#define FCOE_TASK_CLASS_TYPE_3 0
#define FCOE_TASK_CLASS_TYPE_2 1
/* Everest FCoE connection type */
#define B577XX_FCOE_CONNECTION_TYPE 4
/* Error codes for Error Reporting in fast path flows */
/* XFER error codes */
#define FCOE_ERROR_CODE_XFER_OOO_RO 0
#define FCOE_ERROR_CODE_XFER_RO_NOT_ALIGNED 1
#define FCOE_ERROR_CODE_XFER_NULL_BURST_LEN 2
#define FCOE_ERROR_CODE_XFER_RO_GREATER_THAN_DATA2TRNS 3
#define FCOE_ERROR_CODE_XFER_INVALID_PAYLOAD_SIZE 4
#define FCOE_ERROR_CODE_XFER_TASK_TYPE_NOT_WRITE 5
#define FCOE_ERROR_CODE_XFER_PEND_XFER_SET 6
#define FCOE_ERROR_CODE_XFER_OPENED_SEQ 7
#define FCOE_ERROR_CODE_XFER_FCTL 8
/* FCP RSP error codes */
#define FCOE_ERROR_CODE_FCP_RSP_BIDI_FLAGS_SET 9
#define FCOE_ERROR_CODE_FCP_RSP_UNDERFLOW 10
#define FCOE_ERROR_CODE_FCP_RSP_OVERFLOW 11
#define FCOE_ERROR_CODE_FCP_RSP_INVALID_LENGTH_FIELD 12
#define FCOE_ERROR_CODE_FCP_RSP_INVALID_SNS_FIELD 13
#define FCOE_ERROR_CODE_FCP_RSP_INVALID_PAYLOAD_SIZE 14
#define FCOE_ERROR_CODE_FCP_RSP_PEND_XFER_SET 15
#define FCOE_ERROR_CODE_FCP_RSP_OPENED_SEQ 16
#define FCOE_ERROR_CODE_FCP_RSP_FCTL 17
#define FCOE_ERROR_CODE_FCP_RSP_LAST_SEQ_RESET 18
#define FCOE_ERROR_CODE_FCP_RSP_CONF_REQ_NOT_SUPPORTED_YET 19
/* FCP DATA error codes */
#define FCOE_ERROR_CODE_DATA_OOO_RO 20
#define FCOE_ERROR_CODE_DATA_EXCEEDS_DEFINED_MAX_FRAME_SIZE 21
#define FCOE_ERROR_CODE_DATA_EXCEEDS_DATA2TRNS 22
#define FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET 23
#define FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET 24
#define FCOE_ERROR_CODE_DATA_EOFN_END_SEQ_SET 25
#define FCOE_ERROR_CODE_DATA_EOFT_END_SEQ_RESET 26
#define FCOE_ERROR_CODE_DATA_TASK_TYPE_NOT_READ 27
#define FCOE_ERROR_CODE_DATA_FCTL 28
/* Middle path error codes */
#define FCOE_ERROR_CODE_MIDPATH_TYPE_NOT_ELS 29
#define FCOE_ERROR_CODE_MIDPATH_SOFI3_SEQ_ACTIVE_SET 30
#define FCOE_ERROR_CODE_MIDPATH_SOFN_SEQ_ACTIVE_RESET 31
#define FCOE_ERROR_CODE_MIDPATH_EOFN_END_SEQ_SET 32
#define FCOE_ERROR_CODE_MIDPATH_EOFT_END_SEQ_RESET 33
#define FCOE_ERROR_CODE_MIDPATH_ELS_REPLY_FCTL 34
#define FCOE_ERROR_CODE_MIDPATH_INVALID_REPLY 35
#define FCOE_ERROR_CODE_MIDPATH_ELS_REPLY_RCTL 36
/* ABTS error codes */
#define FCOE_ERROR_CODE_ABTS_REPLY_F_CTL 37
#define FCOE_ERROR_CODE_ABTS_REPLY_DDF_RCTL_FIELD 38
#define FCOE_ERROR_CODE_ABTS_REPLY_INVALID_BLS_RCTL 39
#define FCOE_ERROR_CODE_ABTS_REPLY_INVALID_RCTL 40
#define FCOE_ERROR_CODE_ABTS_REPLY_RCTL_GENERAL_MISMATCH 41
/* Common error codes */
#define FCOE_ERROR_CODE_COMMON_MIDDLE_FRAME_WITH_PAD 42
#define FCOE_ERROR_CODE_COMMON_SEQ_INIT_IN_TCE 43
#define FCOE_ERROR_CODE_COMMON_FC_HDR_RX_ID_MISMATCH 44
#define FCOE_ERROR_CODE_COMMON_INCORRECT_SEQ_CNT 45
#define FCOE_ERROR_CODE_COMMON_DATA_FC_HDR_FCP_TYPE_MISMATCH 46
#define FCOE_ERROR_CODE_COMMON_DATA_NO_MORE_SGES 47
#define FCOE_ERROR_CODE_COMMON_OPTIONAL_FC_HDR 48
#define FCOE_ERROR_CODE_COMMON_READ_TCE_OX_ID_TOO_BIG 49
#define FCOE_ERROR_CODE_COMMON_DATA_WAS_NOT_TRANSMITTED 50
/* Unsolicited Rx error codes */
#define FCOE_ERROR_CODE_UNSOLICITED_TYPE_NOT_ELS 51
#define FCOE_ERROR_CODE_UNSOLICITED_TYPE_NOT_BLS 52
#define FCOE_ERROR_CODE_UNSOLICITED_FCTL_ELS 53
#define FCOE_ERROR_CODE_UNSOLICITED_FCTL_BLS 54
#define FCOE_ERROR_CODE_UNSOLICITED_R_CTL 55
#define FCOE_ERROR_CODE_RW_TASK_DDF_RCTL_INFO_FIELD 56
#define FCOE_ERROR_CODE_RW_TASK_INVALID_RCTL 57
#define FCOE_ERROR_CODE_RW_TASK_RCTL_GENERAL_MISMATCH 58
/* Timer error codes */
#define FCOE_ERROR_CODE_E_D_TOV_TIMER_EXPIRATION 60
#define FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION 61
#endif /* BNX2FC_CONSTANTS_H_ */

View file

@ -0,0 +1,70 @@
#ifndef __BNX2FC_DEBUG__
#define __BNX2FC_DEBUG__
/* Log level bit mask */
#define LOG_IO 0x01 /* scsi cmd error, cleanup */
#define LOG_TGT 0x02 /* Session setup, cleanup, etc' */
#define LOG_HBA 0x04 /* lport events, link, mtu, etc' */
#define LOG_ELS 0x08 /* ELS logs */
#define LOG_MISC 0x10 /* fcoe L2 frame related logs*/
#define LOG_ALL 0xff /* LOG all messages */
extern unsigned int bnx2fc_debug_level;
#define BNX2FC_CHK_LOGGING(LEVEL, CMD) \
do { \
if (unlikely(bnx2fc_debug_level & LEVEL)) \
do { \
CMD; \
} while (0); \
} while (0)
#define BNX2FC_ELS_DBG(fmt, arg...) \
BNX2FC_CHK_LOGGING(LOG_ELS, \
printk(KERN_ALERT PFX fmt, ##arg))
#define BNX2FC_MISC_DBG(fmt, arg...) \
BNX2FC_CHK_LOGGING(LOG_MISC, \
printk(KERN_ALERT PFX fmt, ##arg))
#define BNX2FC_IO_DBG(io_req, fmt, arg...) \
do { \
if (!io_req || !io_req->port || !io_req->port->lport || \
!io_req->port->lport->host) \
BNX2FC_CHK_LOGGING(LOG_IO, \
printk(KERN_ALERT PFX "NULL " fmt, ##arg)); \
else \
BNX2FC_CHK_LOGGING(LOG_IO, \
shost_printk(KERN_ALERT, \
(io_req)->port->lport->host, \
PFX "xid:0x%x " fmt, \
(io_req)->xid, ##arg)); \
} while (0)
#define BNX2FC_TGT_DBG(tgt, fmt, arg...) \
do { \
if (!tgt || !tgt->port || !tgt->port->lport || \
!tgt->port->lport->host || !tgt->rport) \
BNX2FC_CHK_LOGGING(LOG_TGT, \
printk(KERN_ALERT PFX "NULL " fmt, ##arg)); \
else \
BNX2FC_CHK_LOGGING(LOG_TGT, \
shost_printk(KERN_ALERT, \
(tgt)->port->lport->host, \
PFX "port:%x " fmt, \
(tgt)->rport->port_id, ##arg)); \
} while (0)
#define BNX2FC_HBA_DBG(lport, fmt, arg...) \
do { \
if (!lport || !lport->host) \
BNX2FC_CHK_LOGGING(LOG_HBA, \
printk(KERN_ALERT PFX "NULL " fmt, ##arg)); \
else \
BNX2FC_CHK_LOGGING(LOG_HBA, \
shost_printk(KERN_ALERT, lport->host, \
PFX fmt, ##arg)); \
} while (0)
#endif

View file

@ -0,0 +1,515 @@
/*
* bnx2fc_els.c: Broadcom NetXtreme II Linux FCoE offload driver.
* This file contains helper routines that handle ELS requests
* and responses.
*
* Copyright (c) 2008 - 2010 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: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
*/
#include "bnx2fc.h"
static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg);
static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg);
static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
void *data, u32 data_len,
void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg),
struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec);
static void bnx2fc_rrq_compl(struct bnx2fc_els_cb_arg *cb_arg)
{
struct bnx2fc_cmd *orig_io_req;
struct bnx2fc_cmd *rrq_req;
int rc = 0;
BUG_ON(!cb_arg);
rrq_req = cb_arg->io_req;
orig_io_req = cb_arg->aborted_io_req;
BUG_ON(!orig_io_req);
BNX2FC_ELS_DBG("rrq_compl: orig xid = 0x%x, rrq_xid = 0x%x\n",
orig_io_req->xid, rrq_req->xid);
kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rrq_req->req_flags)) {
/*
* els req is timed out. cleanup the IO with FW and
* drop the completion. Remove from active_cmd_queue.
*/
BNX2FC_ELS_DBG("rrq xid - 0x%x timed out, clean it up\n",
rrq_req->xid);
if (rrq_req->on_active_queue) {
list_del_init(&rrq_req->link);
rrq_req->on_active_queue = 0;
rc = bnx2fc_initiate_cleanup(rrq_req);
BUG_ON(rc);
}
}
kfree(cb_arg);
}
int bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req)
{
struct fc_els_rrq rrq;
struct bnx2fc_rport *tgt = aborted_io_req->tgt;
struct fc_lport *lport = tgt->rdata->local_port;
struct bnx2fc_els_cb_arg *cb_arg = NULL;
u32 sid = tgt->sid;
u32 r_a_tov = lport->r_a_tov;
unsigned long start = jiffies;
int rc;
BNX2FC_ELS_DBG("Sending RRQ orig_xid = 0x%x\n",
aborted_io_req->xid);
memset(&rrq, 0, sizeof(rrq));
cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_NOIO);
if (!cb_arg) {
printk(KERN_ERR PFX "Unable to allocate cb_arg for RRQ\n");
rc = -ENOMEM;
goto rrq_err;
}
cb_arg->aborted_io_req = aborted_io_req;
rrq.rrq_cmd = ELS_RRQ;
hton24(rrq.rrq_s_id, sid);
rrq.rrq_ox_id = htons(aborted_io_req->xid);
rrq.rrq_rx_id = htons(aborted_io_req->task->rx_wr_tx_rd.rx_id);
retry_rrq:
rc = bnx2fc_initiate_els(tgt, ELS_RRQ, &rrq, sizeof(rrq),
bnx2fc_rrq_compl, cb_arg,
r_a_tov);
if (rc == -ENOMEM) {
if (time_after(jiffies, start + (10 * HZ))) {
BNX2FC_ELS_DBG("rrq Failed\n");
rc = FAILED;
goto rrq_err;
}
msleep(20);
goto retry_rrq;
}
rrq_err:
if (rc) {
BNX2FC_ELS_DBG("RRQ failed - release orig io req 0x%x\n",
aborted_io_req->xid);
kfree(cb_arg);
spin_lock_bh(&tgt->tgt_lock);
kref_put(&aborted_io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
}
return rc;
}
static void bnx2fc_l2_els_compl(struct bnx2fc_els_cb_arg *cb_arg)
{
struct bnx2fc_cmd *els_req;
struct bnx2fc_rport *tgt;
struct bnx2fc_mp_req *mp_req;
struct fc_frame_header *fc_hdr;
unsigned char *buf;
void *resp_buf;
u32 resp_len, hdr_len;
u16 l2_oxid;
int frame_len;
int rc = 0;
l2_oxid = cb_arg->l2_oxid;
BNX2FC_ELS_DBG("ELS COMPL - l2_oxid = 0x%x\n", l2_oxid);
els_req = cb_arg->io_req;
if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &els_req->req_flags)) {
/*
* els req is timed out. cleanup the IO with FW and
* drop the completion. libfc will handle the els timeout
*/
if (els_req->on_active_queue) {
list_del_init(&els_req->link);
els_req->on_active_queue = 0;
rc = bnx2fc_initiate_cleanup(els_req);
BUG_ON(rc);
}
goto free_arg;
}
tgt = els_req->tgt;
mp_req = &(els_req->mp_req);
fc_hdr = &(mp_req->resp_fc_hdr);
resp_len = mp_req->resp_len;
resp_buf = mp_req->resp_buf;
buf = kzalloc(PAGE_SIZE, GFP_ATOMIC);
if (!buf) {
printk(KERN_ERR PFX "Unable to alloc mp buf\n");
goto free_arg;
}
hdr_len = sizeof(*fc_hdr);
if (hdr_len + resp_len > PAGE_SIZE) {
printk(KERN_ERR PFX "l2_els_compl: resp len is "
"beyond page size\n");
goto free_buf;
}
memcpy(buf, fc_hdr, hdr_len);
memcpy(buf + hdr_len, resp_buf, resp_len);
frame_len = hdr_len + resp_len;
bnx2fc_process_l2_frame_compl(tgt, buf, frame_len, l2_oxid);
free_buf:
kfree(buf);
free_arg:
kfree(cb_arg);
}
int bnx2fc_send_adisc(struct bnx2fc_rport *tgt, struct fc_frame *fp)
{
struct fc_els_adisc *adisc;
struct fc_frame_header *fh;
struct bnx2fc_els_cb_arg *cb_arg;
struct fc_lport *lport = tgt->rdata->local_port;
u32 r_a_tov = lport->r_a_tov;
int rc;
fh = fc_frame_header_get(fp);
cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
if (!cb_arg) {
printk(KERN_ERR PFX "Unable to allocate cb_arg for ADISC\n");
return -ENOMEM;
}
cb_arg->l2_oxid = ntohs(fh->fh_ox_id);
BNX2FC_ELS_DBG("send ADISC: l2_oxid = 0x%x\n", cb_arg->l2_oxid);
adisc = fc_frame_payload_get(fp, sizeof(*adisc));
/* adisc is initialized by libfc */
rc = bnx2fc_initiate_els(tgt, ELS_ADISC, adisc, sizeof(*adisc),
bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov);
if (rc)
kfree(cb_arg);
return rc;
}
int bnx2fc_send_logo(struct bnx2fc_rport *tgt, struct fc_frame *fp)
{
struct fc_els_logo *logo;
struct fc_frame_header *fh;
struct bnx2fc_els_cb_arg *cb_arg;
struct fc_lport *lport = tgt->rdata->local_port;
u32 r_a_tov = lport->r_a_tov;
int rc;
fh = fc_frame_header_get(fp);
cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
if (!cb_arg) {
printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n");
return -ENOMEM;
}
cb_arg->l2_oxid = ntohs(fh->fh_ox_id);
BNX2FC_ELS_DBG("Send LOGO: l2_oxid = 0x%x\n", cb_arg->l2_oxid);
logo = fc_frame_payload_get(fp, sizeof(*logo));
/* logo is initialized by libfc */
rc = bnx2fc_initiate_els(tgt, ELS_LOGO, logo, sizeof(*logo),
bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov);
if (rc)
kfree(cb_arg);
return rc;
}
int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp)
{
struct fc_els_rls *rls;
struct fc_frame_header *fh;
struct bnx2fc_els_cb_arg *cb_arg;
struct fc_lport *lport = tgt->rdata->local_port;
u32 r_a_tov = lport->r_a_tov;
int rc;
fh = fc_frame_header_get(fp);
cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
if (!cb_arg) {
printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n");
return -ENOMEM;
}
cb_arg->l2_oxid = ntohs(fh->fh_ox_id);
rls = fc_frame_payload_get(fp, sizeof(*rls));
/* rls is initialized by libfc */
rc = bnx2fc_initiate_els(tgt, ELS_RLS, rls, sizeof(*rls),
bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov);
if (rc)
kfree(cb_arg);
return rc;
}
static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
void *data, u32 data_len,
void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg),
struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec)
{
struct fcoe_port *port = tgt->port;
struct bnx2fc_hba *hba = port->priv;
struct fc_rport *rport = tgt->rport;
struct fc_lport *lport = port->lport;
struct bnx2fc_cmd *els_req;
struct bnx2fc_mp_req *mp_req;
struct fc_frame_header *fc_hdr;
struct fcoe_task_ctx_entry *task;
struct fcoe_task_ctx_entry *task_page;
int rc = 0;
int task_idx, index;
u32 did, sid;
u16 xid;
rc = fc_remote_port_chkready(rport);
if (rc) {
printk(KERN_ALERT PFX "els 0x%x: rport not ready\n", op);
rc = -EINVAL;
goto els_err;
}
if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
printk(KERN_ALERT PFX "els 0x%x: link is not ready\n", op);
rc = -EINVAL;
goto els_err;
}
if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) ||
(test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags))) {
printk(KERN_ERR PFX "els 0x%x: tgt not ready\n", op);
rc = -EINVAL;
goto els_err;
}
els_req = bnx2fc_elstm_alloc(tgt, BNX2FC_ELS);
if (!els_req) {
rc = -ENOMEM;
goto els_err;
}
els_req->sc_cmd = NULL;
els_req->port = port;
els_req->tgt = tgt;
els_req->cb_func = cb_func;
cb_arg->io_req = els_req;
els_req->cb_arg = cb_arg;
mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req);
rc = bnx2fc_init_mp_req(els_req);
if (rc == FAILED) {
printk(KERN_ALERT PFX "ELS MP request init failed\n");
spin_lock_bh(&tgt->tgt_lock);
kref_put(&els_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
rc = -ENOMEM;
goto els_err;
} else {
/* rc SUCCESS */
rc = 0;
}
/* Set the data_xfer_len to the size of ELS payload */
mp_req->req_len = data_len;
els_req->data_xfer_len = mp_req->req_len;
/* Fill ELS Payload */
if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) {
memcpy(mp_req->req_buf, data, data_len);
} else {
printk(KERN_ALERT PFX "Invalid ELS op 0x%x\n", op);
els_req->cb_func = NULL;
els_req->cb_arg = NULL;
spin_lock_bh(&tgt->tgt_lock);
kref_put(&els_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
rc = -EINVAL;
}
if (rc)
goto els_err;
/* Fill FC header */
fc_hdr = &(mp_req->req_fc_hdr);
did = tgt->rport->port_id;
sid = tgt->sid;
__fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid,
FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ |
FC_FC_SEQ_INIT, 0);
/* Obtain exchange id */
xid = els_req->xid;
task_idx = xid/BNX2FC_TASKS_PER_PAGE;
index = xid % BNX2FC_TASKS_PER_PAGE;
/* Initialize task context for this IO request */
task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
task = &(task_page[index]);
bnx2fc_init_mp_task(els_req, task);
spin_lock_bh(&tgt->tgt_lock);
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
printk(KERN_ERR PFX "initiate_els.. session not ready\n");
els_req->cb_func = NULL;
els_req->cb_arg = NULL;
kref_put(&els_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
return -EINVAL;
}
if (timer_msec)
bnx2fc_cmd_timer_set(els_req, timer_msec);
bnx2fc_add_2_sq(tgt, xid);
els_req->on_active_queue = 1;
list_add_tail(&els_req->link, &tgt->els_queue);
/* Ring doorbell */
bnx2fc_ring_doorbell(tgt);
spin_unlock_bh(&tgt->tgt_lock);
els_err:
return rc;
}
void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req,
struct fcoe_task_ctx_entry *task, u8 num_rq)
{
struct bnx2fc_mp_req *mp_req;
struct fc_frame_header *fc_hdr;
u64 *hdr;
u64 *temp_hdr;
BNX2FC_ELS_DBG("Entered process_els_compl xid = 0x%x"
"cmd_type = %d\n", els_req->xid, els_req->cmd_type);
if (test_and_set_bit(BNX2FC_FLAG_ELS_DONE,
&els_req->req_flags)) {
BNX2FC_ELS_DBG("Timer context finished processing this "
"els - 0x%x\n", els_req->xid);
/* This IO doesnt receive cleanup completion */
kref_put(&els_req->refcount, bnx2fc_cmd_release);
return;
}
/* Cancel the timeout_work, as we received the response */
if (cancel_delayed_work(&els_req->timeout_work))
kref_put(&els_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
if (els_req->on_active_queue) {
list_del_init(&els_req->link);
els_req->on_active_queue = 0;
}
mp_req = &(els_req->mp_req);
fc_hdr = &(mp_req->resp_fc_hdr);
hdr = (u64 *)fc_hdr;
temp_hdr = (u64 *)
&task->cmn.general.cmd_info.mp_fc_frame.fc_hdr;
hdr[0] = cpu_to_be64(temp_hdr[0]);
hdr[1] = cpu_to_be64(temp_hdr[1]);
hdr[2] = cpu_to_be64(temp_hdr[2]);
mp_req->resp_len = task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_off;
/* Parse ELS response */
if ((els_req->cb_func) && (els_req->cb_arg)) {
els_req->cb_func(els_req->cb_arg);
els_req->cb_arg = NULL;
}
kref_put(&els_req->refcount, bnx2fc_cmd_release);
}
static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg)
{
struct fcoe_ctlr *fip = arg;
struct fc_exch *exch = fc_seq_exch(seq);
struct fc_lport *lport = exch->lp;
u8 *mac;
struct fc_frame_header *fh;
u8 op;
if (IS_ERR(fp))
goto done;
mac = fr_cb(fp)->granted_mac;
if (is_zero_ether_addr(mac)) {
fh = fc_frame_header_get(fp);
if (fh->fh_type != FC_TYPE_ELS) {
printk(KERN_ERR PFX "bnx2fc_flogi_resp:"
"fh_type != FC_TYPE_ELS\n");
fc_frame_free(fp);
return;
}
op = fc_frame_payload_op(fp);
if (lport->vport) {
if (op == ELS_LS_RJT) {
printk(KERN_ERR PFX "bnx2fc_flogi_resp is LS_RJT\n");
fc_vport_terminate(lport->vport);
fc_frame_free(fp);
return;
}
}
if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
fc_frame_free(fp);
return;
}
}
fip->update_mac(lport, mac);
done:
fc_lport_flogi_resp(seq, fp, lport);
}
static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg)
{
struct fcoe_ctlr *fip = arg;
struct fc_exch *exch = fc_seq_exch(seq);
struct fc_lport *lport = exch->lp;
static u8 zero_mac[ETH_ALEN] = { 0 };
if (!IS_ERR(fp))
fip->update_mac(lport, zero_mac);
fc_lport_logo_resp(seq, fp, lport);
}
struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did,
struct fc_frame *fp, unsigned int op,
void (*resp)(struct fc_seq *,
struct fc_frame *,
void *),
void *arg, u32 timeout)
{
struct fcoe_port *port = lport_priv(lport);
struct bnx2fc_hba *hba = port->priv;
struct fcoe_ctlr *fip = &hba->ctlr;
struct fc_frame_header *fh = fc_frame_header_get(fp);
switch (op) {
case ELS_FLOGI:
case ELS_FDISC:
return fc_elsct_send(lport, did, fp, op, bnx2fc_flogi_resp,
fip, timeout);
case ELS_LOGO:
/* only hook onto fabric logouts, not port logouts */
if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
break;
return fc_elsct_send(lport, did, fp, op, bnx2fc_logo_resp,
fip, timeout);
}
return fc_elsct_send(lport, did, fp, op, resp, arg, timeout);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,844 @@
/* bnx2fc_tgt.c: Broadcom NetXtreme II Linux FCoE offload driver.
* Handles operations such as session offload/upload etc, and manages
* session resources such as connection id and qp resources.
*
* Copyright (c) 2008 - 2010 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: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
*/
#include "bnx2fc.h"
static void bnx2fc_upld_timer(unsigned long data);
static void bnx2fc_ofld_timer(unsigned long data);
static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
struct fcoe_port *port,
struct fc_rport_priv *rdata);
static u32 bnx2fc_alloc_conn_id(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
static void bnx2fc_free_conn_id(struct bnx2fc_hba *hba, u32 conn_id);
static void bnx2fc_upld_timer(unsigned long data)
{
struct bnx2fc_rport *tgt = (struct bnx2fc_rport *)data;
BNX2FC_TGT_DBG(tgt, "upld_timer - Upload compl not received!!\n");
/* fake upload completion */
clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
wake_up_interruptible(&tgt->upld_wait);
}
static void bnx2fc_ofld_timer(unsigned long data)
{
struct bnx2fc_rport *tgt = (struct bnx2fc_rport *)data;
BNX2FC_TGT_DBG(tgt, "entered bnx2fc_ofld_timer\n");
/* NOTE: This function should never be called, as
* offload should never timeout
*/
/*
* If the timer has expired, this session is dead
* Clear offloaded flag and logout of this device.
* Since OFFLOADED flag is cleared, this case
* will be considered as offload error and the
* port will be logged off, and conn_id, session
* resources are freed up in bnx2fc_offload_session
*/
clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
wake_up_interruptible(&tgt->ofld_wait);
}
static void bnx2fc_offload_session(struct fcoe_port *port,
struct bnx2fc_rport *tgt,
struct fc_rport_priv *rdata)
{
struct fc_lport *lport = rdata->local_port;
struct fc_rport *rport = rdata->rport;
struct bnx2fc_hba *hba = port->priv;
int rval;
int i = 0;
/* Initialize bnx2fc_rport */
/* NOTE: tgt is already bzero'd */
rval = bnx2fc_init_tgt(tgt, port, rdata);
if (rval) {
printk(KERN_ERR PFX "Failed to allocate conn id for "
"port_id (%6x)\n", rport->port_id);
goto ofld_err;
}
/* Allocate session resources */
rval = bnx2fc_alloc_session_resc(hba, tgt);
if (rval) {
printk(KERN_ERR PFX "Failed to allocate resources\n");
goto ofld_err;
}
/*
* Initialize FCoE session offload process.
* Upon completion of offload process add
* rport to list of rports
*/
retry_ofld:
clear_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
rval = bnx2fc_send_session_ofld_req(port, tgt);
if (rval) {
printk(KERN_ERR PFX "ofld_req failed\n");
goto ofld_err;
}
/*
* wait for the session is offloaded and enabled. 3 Secs
* should be ample time for this process to complete.
*/
setup_timer(&tgt->ofld_timer, bnx2fc_ofld_timer, (unsigned long)tgt);
mod_timer(&tgt->ofld_timer, jiffies + BNX2FC_FW_TIMEOUT);
wait_event_interruptible(tgt->ofld_wait,
(test_bit(
BNX2FC_FLAG_OFLD_REQ_CMPL,
&tgt->flags)));
if (signal_pending(current))
flush_signals(current);
del_timer_sync(&tgt->ofld_timer);
if (!(test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags))) {
if (test_and_clear_bit(BNX2FC_FLAG_CTX_ALLOC_FAILURE,
&tgt->flags)) {
BNX2FC_TGT_DBG(tgt, "ctx_alloc_failure, "
"retry ofld..%d\n", i++);
msleep_interruptible(1000);
if (i > 3) {
i = 0;
goto ofld_err;
}
goto retry_ofld;
}
goto ofld_err;
}
if (bnx2fc_map_doorbell(tgt)) {
printk(KERN_ERR PFX "map doorbell failed - no mem\n");
/* upload will take care of cleaning up sess resc */
lport->tt.rport_logoff(rdata);
}
return;
ofld_err:
/* couldn't offload the session. log off from this rport */
BNX2FC_TGT_DBG(tgt, "bnx2fc_offload_session - offload error\n");
lport->tt.rport_logoff(rdata);
/* Free session resources */
bnx2fc_free_session_resc(hba, tgt);
if (tgt->fcoe_conn_id != -1)
bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id);
}
void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
{
struct bnx2fc_cmd *io_req;
struct list_head *list;
struct list_head *tmp;
int rc;
int i = 0;
BNX2FC_TGT_DBG(tgt, "Entered flush_active_ios - %d\n",
tgt->num_active_ios.counter);
spin_lock_bh(&tgt->tgt_lock);
tgt->flush_in_prog = 1;
list_for_each_safe(list, tmp, &tgt->active_cmd_queue) {
i++;
io_req = (struct bnx2fc_cmd *)list;
list_del_init(&io_req->link);
io_req->on_active_queue = 0;
BNX2FC_IO_DBG(io_req, "cmd_queue cleanup\n");
if (cancel_delayed_work(&io_req->timeout_work)) {
if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT,
&io_req->req_flags)) {
/* Handle eh_abort timeout */
BNX2FC_IO_DBG(io_req, "eh_abort for IO "
"cleaned up\n");
complete(&io_req->tm_done);
}
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
}
set_bit(BNX2FC_FLAG_IO_COMPL, &io_req->req_flags);
set_bit(BNX2FC_FLAG_IO_CLEANUP, &io_req->req_flags);
rc = bnx2fc_initiate_cleanup(io_req);
BUG_ON(rc);
}
list_for_each_safe(list, tmp, &tgt->els_queue) {
i++;
io_req = (struct bnx2fc_cmd *)list;
list_del_init(&io_req->link);
io_req->on_active_queue = 0;
BNX2FC_IO_DBG(io_req, "els_queue cleanup\n");
if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
if ((io_req->cb_func) && (io_req->cb_arg)) {
io_req->cb_func(io_req->cb_arg);
io_req->cb_arg = NULL;
}
rc = bnx2fc_initiate_cleanup(io_req);
BUG_ON(rc);
}
list_for_each_safe(list, tmp, &tgt->io_retire_queue) {
i++;
io_req = (struct bnx2fc_cmd *)list;
list_del_init(&io_req->link);
BNX2FC_IO_DBG(io_req, "retire_queue flush\n");
if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount, bnx2fc_cmd_release);
clear_bit(BNX2FC_FLAG_ISSUE_RRQ, &io_req->req_flags);
}
BNX2FC_TGT_DBG(tgt, "IOs flushed = %d\n", i);
i = 0;
spin_unlock_bh(&tgt->tgt_lock);
/* wait for active_ios to go to 0 */
while ((tgt->num_active_ios.counter != 0) && (i++ < BNX2FC_WAIT_CNT))
msleep(25);
if (tgt->num_active_ios.counter != 0)
printk(KERN_ERR PFX "CLEANUP on port 0x%x:"
" active_ios = %d\n",
tgt->rdata->ids.port_id, tgt->num_active_ios.counter);
spin_lock_bh(&tgt->tgt_lock);
tgt->flush_in_prog = 0;
spin_unlock_bh(&tgt->tgt_lock);
}
static void bnx2fc_upload_session(struct fcoe_port *port,
struct bnx2fc_rport *tgt)
{
struct bnx2fc_hba *hba = port->priv;
BNX2FC_TGT_DBG(tgt, "upload_session: active_ios = %d\n",
tgt->num_active_ios.counter);
/*
* Called with hba->hba_mutex held.
* This is a blocking call
*/
clear_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
bnx2fc_send_session_disable_req(port, tgt);
/*
* wait for upload to complete. 3 Secs
* should be sufficient time for this process to complete.
*/
setup_timer(&tgt->upld_timer, bnx2fc_upld_timer, (unsigned long)tgt);
mod_timer(&tgt->upld_timer, jiffies + BNX2FC_FW_TIMEOUT);
BNX2FC_TGT_DBG(tgt, "waiting for disable compl\n");
wait_event_interruptible(tgt->upld_wait,
(test_bit(
BNX2FC_FLAG_UPLD_REQ_COMPL,
&tgt->flags)));
if (signal_pending(current))
flush_signals(current);
del_timer_sync(&tgt->upld_timer);
/*
* traverse thru the active_q and tmf_q and cleanup
* IOs in these lists
*/
BNX2FC_TGT_DBG(tgt, "flush/upload - disable wait flags = 0x%lx\n",
tgt->flags);
bnx2fc_flush_active_ios(tgt);
/* Issue destroy KWQE */
if (test_bit(BNX2FC_FLAG_DISABLED, &tgt->flags)) {
BNX2FC_TGT_DBG(tgt, "send destroy req\n");
clear_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
bnx2fc_send_session_destroy_req(hba, tgt);
/* wait for destroy to complete */
setup_timer(&tgt->upld_timer,
bnx2fc_upld_timer, (unsigned long)tgt);
mod_timer(&tgt->upld_timer, jiffies + BNX2FC_FW_TIMEOUT);
wait_event_interruptible(tgt->upld_wait,
(test_bit(
BNX2FC_FLAG_UPLD_REQ_COMPL,
&tgt->flags)));
if (!(test_bit(BNX2FC_FLAG_DESTROYED, &tgt->flags)))
printk(KERN_ERR PFX "ERROR!! destroy timed out\n");
BNX2FC_TGT_DBG(tgt, "destroy wait complete flags = 0x%lx\n",
tgt->flags);
if (signal_pending(current))
flush_signals(current);
del_timer_sync(&tgt->upld_timer);
} else
printk(KERN_ERR PFX "ERROR!! DISABLE req timed out, destroy"
" not sent to FW\n");
/* Free session resources */
spin_lock_bh(&tgt->cq_lock);
bnx2fc_free_session_resc(hba, tgt);
bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id);
spin_unlock_bh(&tgt->cq_lock);
}
static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
struct fcoe_port *port,
struct fc_rport_priv *rdata)
{
struct fc_rport *rport = rdata->rport;
struct bnx2fc_hba *hba = port->priv;
tgt->rport = rport;
tgt->rdata = rdata;
tgt->port = port;
if (hba->num_ofld_sess >= BNX2FC_NUM_MAX_SESS) {
BNX2FC_TGT_DBG(tgt, "exceeded max sessions. logoff this tgt\n");
tgt->fcoe_conn_id = -1;
return -1;
}
tgt->fcoe_conn_id = bnx2fc_alloc_conn_id(hba, tgt);
if (tgt->fcoe_conn_id == -1)
return -1;
BNX2FC_TGT_DBG(tgt, "init_tgt - conn_id = 0x%x\n", tgt->fcoe_conn_id);
tgt->max_sqes = BNX2FC_SQ_WQES_MAX;
tgt->max_rqes = BNX2FC_RQ_WQES_MAX;
tgt->max_cqes = BNX2FC_CQ_WQES_MAX;
/* Initialize the toggle bit */
tgt->sq_curr_toggle_bit = 1;
tgt->cq_curr_toggle_bit = 1;
tgt->sq_prod_idx = 0;
tgt->cq_cons_idx = 0;
tgt->rq_prod_idx = 0x8000;
tgt->rq_cons_idx = 0;
atomic_set(&tgt->num_active_ios, 0);
tgt->work_time_slice = 2;
spin_lock_init(&tgt->tgt_lock);
spin_lock_init(&tgt->cq_lock);
/* Initialize active_cmd_queue list */
INIT_LIST_HEAD(&tgt->active_cmd_queue);
/* Initialize IO retire queue */
INIT_LIST_HEAD(&tgt->io_retire_queue);
INIT_LIST_HEAD(&tgt->els_queue);
/* Initialize active_tm_queue list */
INIT_LIST_HEAD(&tgt->active_tm_queue);
init_waitqueue_head(&tgt->ofld_wait);
init_waitqueue_head(&tgt->upld_wait);
return 0;
}
/**
* This event_callback is called after successful completion of libfc
* initiated target login. bnx2fc can proceed with initiating the session
* establishment.
*/
void bnx2fc_rport_event_handler(struct fc_lport *lport,
struct fc_rport_priv *rdata,
enum fc_rport_event event)
{
struct fcoe_port *port = lport_priv(lport);
struct bnx2fc_hba *hba = port->priv;
struct fc_rport *rport = rdata->rport;
struct fc_rport_libfc_priv *rp;
struct bnx2fc_rport *tgt;
u32 port_id;
BNX2FC_HBA_DBG(lport, "rport_event_hdlr: event = %d, port_id = 0x%x\n",
event, rdata->ids.port_id);
switch (event) {
case RPORT_EV_READY:
if (!rport) {
printk(KERN_ALERT PFX "rport is NULL: ERROR!\n");
break;
}
rp = rport->dd_data;
if (rport->port_id == FC_FID_DIR_SERV) {
/*
* bnx2fc_rport structure doesnt exist for
* directory server.
* We should not come here, as lport will
* take care of fabric login
*/
printk(KERN_ALERT PFX "%x - rport_event_handler ERROR\n",
rdata->ids.port_id);
break;
}
if (rdata->spp_type != FC_TYPE_FCP) {
BNX2FC_HBA_DBG(lport, "not FCP type target."
" not offloading\n");
break;
}
if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) {
BNX2FC_HBA_DBG(lport, "not FCP_TARGET"
" not offloading\n");
break;
}
/*
* Offlaod process is protected with hba mutex.
* Use the same mutex_lock for upload process too
*/
mutex_lock(&hba->hba_mutex);
tgt = (struct bnx2fc_rport *)&rp[1];
/* This can happen when ADISC finds the same target */
if (test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags)) {
BNX2FC_TGT_DBG(tgt, "already offloaded\n");
mutex_unlock(&hba->hba_mutex);
return;
}
/*
* Offload the session. This is a blocking call, and will
* wait until the session is offloaded.
*/
bnx2fc_offload_session(port, tgt, rdata);
BNX2FC_TGT_DBG(tgt, "OFFLOAD num_ofld_sess = %d\n",
hba->num_ofld_sess);
if (test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags)) {
/*
* Session is offloaded and enabled. Map
* doorbell register for this target
*/
BNX2FC_TGT_DBG(tgt, "sess offloaded\n");
/* This counter is protected with hba mutex */
hba->num_ofld_sess++;
set_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags);
} else {
/*
* Offload or enable would have failed.
* In offload/enable completion path, the
* rport would have already been removed
*/
BNX2FC_TGT_DBG(tgt, "Port is being logged off as "
"offloaded flag not set\n");
}
mutex_unlock(&hba->hba_mutex);
break;
case RPORT_EV_LOGO:
case RPORT_EV_FAILED:
case RPORT_EV_STOP:
port_id = rdata->ids.port_id;
if (port_id == FC_FID_DIR_SERV)
break;
if (!rport) {
printk(KERN_ALERT PFX "%x - rport not created Yet!!\n",
port_id);
break;
}
rp = rport->dd_data;
mutex_lock(&hba->hba_mutex);
/*
* Perform session upload. Note that rdata->peers is already
* removed from disc->rports list before we get this event.
*/
tgt = (struct bnx2fc_rport *)&rp[1];
if (!(test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags))) {
mutex_unlock(&hba->hba_mutex);
break;
}
clear_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags);
bnx2fc_upload_session(port, tgt);
hba->num_ofld_sess--;
BNX2FC_TGT_DBG(tgt, "UPLOAD num_ofld_sess = %d\n",
hba->num_ofld_sess);
/*
* Try to wake up the linkdown wait thread. If num_ofld_sess
* is 0, the waiting therad wakes up
*/
if ((hba->wait_for_link_down) &&
(hba->num_ofld_sess == 0)) {
wake_up_interruptible(&hba->shutdown_wait);
}
if (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags)) {
printk(KERN_ERR PFX "Relogin to the tgt\n");
mutex_lock(&lport->disc.disc_mutex);
lport->tt.rport_login(rdata);
mutex_unlock(&lport->disc.disc_mutex);
}
mutex_unlock(&hba->hba_mutex);
break;
case RPORT_EV_NONE:
break;
}
}
/**
* bnx2fc_tgt_lookup() - Lookup a bnx2fc_rport by port_id
*
* @port: fcoe_port struct to lookup the target port on
* @port_id: The remote port ID to look up
*/
struct bnx2fc_rport *bnx2fc_tgt_lookup(struct fcoe_port *port,
u32 port_id)
{
struct bnx2fc_hba *hba = port->priv;
struct bnx2fc_rport *tgt;
struct fc_rport_priv *rdata;
int i;
for (i = 0; i < BNX2FC_NUM_MAX_SESS; i++) {
tgt = hba->tgt_ofld_list[i];
if ((tgt) && (tgt->port == port)) {
rdata = tgt->rdata;
if (rdata->ids.port_id == port_id) {
if (rdata->rp_state != RPORT_ST_DELETE) {
BNX2FC_TGT_DBG(tgt, "rport "
"obtained\n");
return tgt;
} else {
printk(KERN_ERR PFX "rport 0x%x "
"is in DELETED state\n",
rdata->ids.port_id);
return NULL;
}
}
}
}
return NULL;
}
/**
* bnx2fc_alloc_conn_id - allocates FCOE Connection id
*
* @hba: pointer to adapter structure
* @tgt: pointer to bnx2fc_rport structure
*/
static u32 bnx2fc_alloc_conn_id(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt)
{
u32 conn_id, next;
/* called with hba mutex held */
/*
* tgt_ofld_list access is synchronized using
* both hba mutex and hba lock. Atleast hba mutex or
* hba lock needs to be held for read access.
*/
spin_lock_bh(&hba->hba_lock);
next = hba->next_conn_id;
conn_id = hba->next_conn_id++;
if (hba->next_conn_id == BNX2FC_NUM_MAX_SESS)
hba->next_conn_id = 0;
while (hba->tgt_ofld_list[conn_id] != NULL) {
conn_id++;
if (conn_id == BNX2FC_NUM_MAX_SESS)
conn_id = 0;
if (conn_id == next) {
/* No free conn_ids are available */
spin_unlock_bh(&hba->hba_lock);
return -1;
}
}
hba->tgt_ofld_list[conn_id] = tgt;
tgt->fcoe_conn_id = conn_id;
spin_unlock_bh(&hba->hba_lock);
return conn_id;
}
static void bnx2fc_free_conn_id(struct bnx2fc_hba *hba, u32 conn_id)
{
/* called with hba mutex held */
spin_lock_bh(&hba->hba_lock);
hba->tgt_ofld_list[conn_id] = NULL;
hba->next_conn_id = conn_id;
spin_unlock_bh(&hba->hba_lock);
}
/**
*bnx2fc_alloc_session_resc - Allocate qp resources for the session
*
*/
static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt)
{
dma_addr_t page;
int num_pages;
u32 *pbl;
/* Allocate and map SQ */
tgt->sq_mem_size = tgt->max_sqes * BNX2FC_SQ_WQE_SIZE;
tgt->sq_mem_size = (tgt->sq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
tgt->sq = dma_alloc_coherent(&hba->pcidev->dev, tgt->sq_mem_size,
&tgt->sq_dma, GFP_KERNEL);
if (!tgt->sq) {
printk(KERN_ALERT PFX "unable to allocate SQ memory %d\n",
tgt->sq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->sq, 0, tgt->sq_mem_size);
/* Allocate and map CQ */
tgt->cq_mem_size = tgt->max_cqes * BNX2FC_CQ_WQE_SIZE;
tgt->cq_mem_size = (tgt->cq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
tgt->cq = dma_alloc_coherent(&hba->pcidev->dev, tgt->cq_mem_size,
&tgt->cq_dma, GFP_KERNEL);
if (!tgt->cq) {
printk(KERN_ALERT PFX "unable to allocate CQ memory %d\n",
tgt->cq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->cq, 0, tgt->cq_mem_size);
/* Allocate and map RQ and RQ PBL */
tgt->rq_mem_size = tgt->max_rqes * BNX2FC_RQ_WQE_SIZE;
tgt->rq_mem_size = (tgt->rq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
tgt->rq = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_mem_size,
&tgt->rq_dma, GFP_KERNEL);
if (!tgt->rq) {
printk(KERN_ALERT PFX "unable to allocate RQ memory %d\n",
tgt->rq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->rq, 0, tgt->rq_mem_size);
tgt->rq_pbl_size = (tgt->rq_mem_size / PAGE_SIZE) * sizeof(void *);
tgt->rq_pbl_size = (tgt->rq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
tgt->rq_pbl = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_pbl_size,
&tgt->rq_pbl_dma, GFP_KERNEL);
if (!tgt->rq_pbl) {
printk(KERN_ALERT PFX "unable to allocate RQ PBL %d\n",
tgt->rq_pbl_size);
goto mem_alloc_failure;
}
memset(tgt->rq_pbl, 0, tgt->rq_pbl_size);
num_pages = tgt->rq_mem_size / PAGE_SIZE;
page = tgt->rq_dma;
pbl = (u32 *)tgt->rq_pbl;
while (num_pages--) {
*pbl = (u32)page;
pbl++;
*pbl = (u32)((u64)page >> 32);
pbl++;
page += PAGE_SIZE;
}
/* Allocate and map XFERQ */
tgt->xferq_mem_size = tgt->max_sqes * BNX2FC_XFERQ_WQE_SIZE;
tgt->xferq_mem_size = (tgt->xferq_mem_size + (PAGE_SIZE - 1)) &
PAGE_MASK;
tgt->xferq = dma_alloc_coherent(&hba->pcidev->dev, tgt->xferq_mem_size,
&tgt->xferq_dma, GFP_KERNEL);
if (!tgt->xferq) {
printk(KERN_ALERT PFX "unable to allocate XFERQ %d\n",
tgt->xferq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->xferq, 0, tgt->xferq_mem_size);
/* Allocate and map CONFQ & CONFQ PBL */
tgt->confq_mem_size = tgt->max_sqes * BNX2FC_CONFQ_WQE_SIZE;
tgt->confq_mem_size = (tgt->confq_mem_size + (PAGE_SIZE - 1)) &
PAGE_MASK;
tgt->confq = dma_alloc_coherent(&hba->pcidev->dev, tgt->confq_mem_size,
&tgt->confq_dma, GFP_KERNEL);
if (!tgt->confq) {
printk(KERN_ALERT PFX "unable to allocate CONFQ %d\n",
tgt->confq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->confq, 0, tgt->confq_mem_size);
tgt->confq_pbl_size =
(tgt->confq_mem_size / PAGE_SIZE) * sizeof(void *);
tgt->confq_pbl_size =
(tgt->confq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
tgt->confq_pbl = dma_alloc_coherent(&hba->pcidev->dev,
tgt->confq_pbl_size,
&tgt->confq_pbl_dma, GFP_KERNEL);
if (!tgt->confq_pbl) {
printk(KERN_ALERT PFX "unable to allocate CONFQ PBL %d\n",
tgt->confq_pbl_size);
goto mem_alloc_failure;
}
memset(tgt->confq_pbl, 0, tgt->confq_pbl_size);
num_pages = tgt->confq_mem_size / PAGE_SIZE;
page = tgt->confq_dma;
pbl = (u32 *)tgt->confq_pbl;
while (num_pages--) {
*pbl = (u32)page;
pbl++;
*pbl = (u32)((u64)page >> 32);
pbl++;
page += PAGE_SIZE;
}
/* Allocate and map ConnDB */
tgt->conn_db_mem_size = sizeof(struct fcoe_conn_db);
tgt->conn_db = dma_alloc_coherent(&hba->pcidev->dev,
tgt->conn_db_mem_size,
&tgt->conn_db_dma, GFP_KERNEL);
if (!tgt->conn_db) {
printk(KERN_ALERT PFX "unable to allocate conn_db %d\n",
tgt->conn_db_mem_size);
goto mem_alloc_failure;
}
memset(tgt->conn_db, 0, tgt->conn_db_mem_size);
/* Allocate and map LCQ */
tgt->lcq_mem_size = (tgt->max_sqes + 8) * BNX2FC_SQ_WQE_SIZE;
tgt->lcq_mem_size = (tgt->lcq_mem_size + (PAGE_SIZE - 1)) &
PAGE_MASK;
tgt->lcq = dma_alloc_coherent(&hba->pcidev->dev, tgt->lcq_mem_size,
&tgt->lcq_dma, GFP_KERNEL);
if (!tgt->lcq) {
printk(KERN_ALERT PFX "unable to allocate lcq %d\n",
tgt->lcq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->lcq, 0, tgt->lcq_mem_size);
/* Arm CQ */
tgt->conn_db->cq_arm.lo = -1;
tgt->conn_db->rq_prod = 0x8000;
return 0;
mem_alloc_failure:
bnx2fc_free_session_resc(hba, tgt);
bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id);
return -ENOMEM;
}
/**
* bnx2i_free_session_resc - free qp resources for the session
*
* @hba: adapter structure pointer
* @tgt: bnx2fc_rport structure pointer
*
* Free QP resources - SQ/RQ/CQ/XFERQ memory and PBL
*/
static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt)
{
BNX2FC_TGT_DBG(tgt, "Freeing up session resources\n");
if (tgt->ctx_base) {
iounmap(tgt->ctx_base);
tgt->ctx_base = NULL;
}
/* Free LCQ */
if (tgt->lcq) {
dma_free_coherent(&hba->pcidev->dev, tgt->lcq_mem_size,
tgt->lcq, tgt->lcq_dma);
tgt->lcq = NULL;
}
/* Free connDB */
if (tgt->conn_db) {
dma_free_coherent(&hba->pcidev->dev, tgt->conn_db_mem_size,
tgt->conn_db, tgt->conn_db_dma);
tgt->conn_db = NULL;
}
/* Free confq and confq pbl */
if (tgt->confq_pbl) {
dma_free_coherent(&hba->pcidev->dev, tgt->confq_pbl_size,
tgt->confq_pbl, tgt->confq_pbl_dma);
tgt->confq_pbl = NULL;
}
if (tgt->confq) {
dma_free_coherent(&hba->pcidev->dev, tgt->confq_mem_size,
tgt->confq, tgt->confq_dma);
tgt->confq = NULL;
}
/* Free XFERQ */
if (tgt->xferq) {
dma_free_coherent(&hba->pcidev->dev, tgt->xferq_mem_size,
tgt->xferq, tgt->xferq_dma);
tgt->xferq = NULL;
}
/* Free RQ PBL and RQ */
if (tgt->rq_pbl) {
dma_free_coherent(&hba->pcidev->dev, tgt->rq_pbl_size,
tgt->rq_pbl, tgt->rq_pbl_dma);
tgt->rq_pbl = NULL;
}
if (tgt->rq) {
dma_free_coherent(&hba->pcidev->dev, tgt->rq_mem_size,
tgt->rq, tgt->rq_dma);
tgt->rq = NULL;
}
/* Free CQ */
if (tgt->cq) {
dma_free_coherent(&hba->pcidev->dev, tgt->cq_mem_size,
tgt->cq, tgt->cq_dma);
tgt->cq = NULL;
}
/* Free SQ */
if (tgt->sq) {
dma_free_coherent(&hba->pcidev->dev, tgt->sq_mem_size,
tgt->sq, tgt->sq_dma);
tgt->sq = NULL;
}
}

View file

@ -360,7 +360,7 @@ struct bnx2i_hba {
#define ADAPTER_STATE_LINK_DOWN 2
#define ADAPTER_STATE_INIT_FAILED 31
unsigned int mtu_supported;
#define BNX2I_MAX_MTU_SUPPORTED 1500
#define BNX2I_MAX_MTU_SUPPORTED 9000
struct Scsi_Host *shost;
@ -751,6 +751,8 @@ extern int bnx2i_send_iscsi_login(struct bnx2i_conn *conn,
struct iscsi_task *mtask);
extern int bnx2i_send_iscsi_tmf(struct bnx2i_conn *conn,
struct iscsi_task *mtask);
extern int bnx2i_send_iscsi_text(struct bnx2i_conn *conn,
struct iscsi_task *mtask);
extern int bnx2i_send_iscsi_scsicmd(struct bnx2i_conn *conn,
struct bnx2i_cmd *cmnd);
extern int bnx2i_send_iscsi_nopout(struct bnx2i_conn *conn,

View file

@ -444,6 +444,56 @@ int bnx2i_send_iscsi_tmf(struct bnx2i_conn *bnx2i_conn,
return 0;
}
/**
* bnx2i_send_iscsi_text - post iSCSI text WQE to hardware
* @conn: iscsi connection
* @mtask: driver command structure which is requesting
* a WQE to sent to chip for further processing
*
* prepare and post an iSCSI Text request WQE to CNIC firmware
*/
int bnx2i_send_iscsi_text(struct bnx2i_conn *bnx2i_conn,
struct iscsi_task *mtask)
{
struct bnx2i_cmd *bnx2i_cmd;
struct bnx2i_text_request *text_wqe;
struct iscsi_text *text_hdr;
u32 dword;
bnx2i_cmd = (struct bnx2i_cmd *)mtask->dd_data;
text_hdr = (struct iscsi_text *)mtask->hdr;
text_wqe = (struct bnx2i_text_request *) bnx2i_conn->ep->qp.sq_prod_qe;
memset(text_wqe, 0, sizeof(struct bnx2i_text_request));
text_wqe->op_code = text_hdr->opcode;
text_wqe->op_attr = text_hdr->flags;
text_wqe->data_length = ntoh24(text_hdr->dlength);
text_wqe->itt = mtask->itt |
(ISCSI_TASK_TYPE_MPATH << ISCSI_TEXT_REQUEST_TYPE_SHIFT);
text_wqe->ttt = be32_to_cpu(text_hdr->ttt);
text_wqe->cmd_sn = be32_to_cpu(text_hdr->cmdsn);
text_wqe->resp_bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_bd_dma;
text_wqe->resp_bd_list_addr_hi =
(u32) ((u64) bnx2i_conn->gen_pdu.resp_bd_dma >> 32);
dword = ((1 << ISCSI_TEXT_REQUEST_NUM_RESP_BDS_SHIFT) |
(bnx2i_conn->gen_pdu.resp_buf_size <<
ISCSI_TEXT_REQUEST_RESP_BUFFER_LENGTH_SHIFT));
text_wqe->resp_buffer = dword;
text_wqe->bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.req_bd_dma;
text_wqe->bd_list_addr_hi =
(u32) ((u64) bnx2i_conn->gen_pdu.req_bd_dma >> 32);
text_wqe->num_bds = 1;
text_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */
bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1);
return 0;
}
/**
* bnx2i_send_iscsi_scsicmd - post iSCSI scsicmd request WQE to hardware
* @conn: iscsi connection
@ -490,15 +540,18 @@ int bnx2i_send_iscsi_nopout(struct bnx2i_conn *bnx2i_conn,
bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data;
nopout_hdr = (struct iscsi_nopout *)task->hdr;
nopout_wqe = (struct bnx2i_nop_out_request *)ep->qp.sq_prod_qe;
memset(nopout_wqe, 0x00, sizeof(struct bnx2i_nop_out_request));
nopout_wqe->op_code = nopout_hdr->opcode;
nopout_wqe->op_attr = ISCSI_FLAG_CMD_FINAL;
memcpy(nopout_wqe->lun, nopout_hdr->lun, 8);
if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) {
u32 tmp = nopout_hdr->lun[0];
u32 tmp = nopout_wqe->lun[0];
/* 57710 requires LUN field to be swapped */
nopout_hdr->lun[0] = nopout_hdr->lun[1];
nopout_hdr->lun[1] = tmp;
nopout_wqe->lun[0] = nopout_wqe->lun[1];
nopout_wqe->lun[1] = tmp;
}
nopout_wqe->itt = ((u16)task->itt |
@ -1425,6 +1478,68 @@ done:
return 0;
}
/**
* bnx2i_process_text_resp - this function handles iscsi text response
* @session: iscsi session pointer
* @bnx2i_conn: iscsi connection pointer
* @cqe: pointer to newly DMA'ed CQE entry for processing
*
* process iSCSI Text Response CQE& complete it to open-iscsi user daemon
*/
static int bnx2i_process_text_resp(struct iscsi_session *session,
struct bnx2i_conn *bnx2i_conn,
struct cqe *cqe)
{
struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data;
struct iscsi_task *task;
struct bnx2i_text_response *text;
struct iscsi_text_rsp *resp_hdr;
int pld_len;
int pad_len;
text = (struct bnx2i_text_response *) cqe;
spin_lock(&session->lock);
task = iscsi_itt_to_task(conn, text->itt & ISCSI_LOGIN_RESPONSE_INDEX);
if (!task)
goto done;
resp_hdr = (struct iscsi_text_rsp *)&bnx2i_conn->gen_pdu.resp_hdr;
memset(resp_hdr, 0, sizeof(struct iscsi_hdr));
resp_hdr->opcode = text->op_code;
resp_hdr->flags = text->response_flags;
resp_hdr->hlength = 0;
hton24(resp_hdr->dlength, text->data_length);
resp_hdr->itt = task->hdr->itt;
resp_hdr->ttt = cpu_to_be32(text->ttt);
resp_hdr->statsn = task->hdr->exp_statsn;
resp_hdr->exp_cmdsn = cpu_to_be32(text->exp_cmd_sn);
resp_hdr->max_cmdsn = cpu_to_be32(text->max_cmd_sn);
pld_len = text->data_length;
bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf +
pld_len;
pad_len = 0;
if (pld_len & 0x3)
pad_len = 4 - (pld_len % 4);
if (pad_len) {
int i = 0;
for (i = 0; i < pad_len; i++) {
bnx2i_conn->gen_pdu.resp_wr_ptr[0] = 0;
bnx2i_conn->gen_pdu.resp_wr_ptr++;
}
}
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr,
bnx2i_conn->gen_pdu.resp_buf,
bnx2i_conn->gen_pdu.resp_wr_ptr -
bnx2i_conn->gen_pdu.resp_buf);
done:
spin_unlock(&session->lock);
return 0;
}
/**
* bnx2i_process_tmf_resp - this function handles iscsi TMF response
* @session: iscsi session pointer
@ -1766,6 +1881,10 @@ static void bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn)
bnx2i_process_tmf_resp(session, bnx2i_conn,
qp->cq_cons_qe);
break;
case ISCSI_OP_TEXT_RSP:
bnx2i_process_text_resp(session, bnx2i_conn,
qp->cq_cons_qe);
break;
case ISCSI_OP_LOGOUT_RSP:
bnx2i_process_logout_resp(session, bnx2i_conn,
qp->cq_cons_qe);

View file

@ -18,8 +18,8 @@ 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.6.2.2"
#define DRV_MODULE_RELDATE "Nov 23, 2010"
#define DRV_MODULE_VERSION "2.6.2.3"
#define DRV_MODULE_RELDATE "Dec 31, 2010"
static char version[] __devinitdata =
"Broadcom NetXtreme II iSCSI Driver " DRV_MODULE_NAME \
@ -29,7 +29,7 @@ static char version[] __devinitdata =
MODULE_AUTHOR("Anil Veerabhadrappa <anilgv@broadcom.com> and "
"Eddie Wai <eddie.wai@broadcom.com>");
MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/57710/57711"
MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/57710/57711/57712"
" iSCSI Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
@ -88,9 +88,11 @@ void bnx2i_identify_device(struct bnx2i_hba *hba)
(hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) {
set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type);
hba->mail_queue_access = BNX2I_MQ_BIN_MODE;
} else if (hba->pci_did == PCI_DEVICE_ID_NX2_57710 ||
hba->pci_did == PCI_DEVICE_ID_NX2_57711 ||
hba->pci_did == PCI_DEVICE_ID_NX2_57711E)
} else if (hba->pci_did == PCI_DEVICE_ID_NX2_57710 ||
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)
set_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type);
else
printk(KERN_ALERT "bnx2i: unknown device, 0x%x\n",
@ -161,6 +163,21 @@ void bnx2i_start(void *handle)
struct bnx2i_hba *hba = handle;
int i = HZ;
if (!hba->cnic->max_iscsi_conn) {
printk(KERN_ALERT "bnx2i: dev %s does not support "
"iSCSI\n", hba->netdev->name);
if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
mutex_lock(&bnx2i_dev_lock);
list_del_init(&hba->link);
adapter_count--;
hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
mutex_unlock(&bnx2i_dev_lock);
bnx2i_free_hba(hba);
}
return;
}
bnx2i_send_fw_iscsi_init_msg(hba);
while (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state) && i--)
msleep(BNX2I_INIT_POLL_TIME);

View file

@ -1092,6 +1092,9 @@ static int bnx2i_iscsi_send_generic_request(struct iscsi_task *task)
case ISCSI_OP_SCSI_TMFUNC:
rc = bnx2i_send_iscsi_tmf(bnx2i_conn, task);
break;
case ISCSI_OP_TEXT:
rc = bnx2i_send_iscsi_text(bnx2i_conn, task);
break;
default:
iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data,
"send_gen: unsupported op 0x%x\n",
@ -1455,42 +1458,40 @@ static void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
/**
* bnx2i_conn_get_param - return iscsi connection parameter to caller
* @cls_conn: pointer to iscsi cls conn
* bnx2i_ep_get_param - return iscsi ep parameter to caller
* @ep: pointer to iscsi endpoint
* @param: parameter type identifier
* @buf: buffer pointer
*
* returns iSCSI connection parameters
* returns iSCSI ep parameters
*/
static int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
static int bnx2i_ep_get_param(struct iscsi_endpoint *ep,
enum iscsi_param param, char *buf)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct bnx2i_conn *bnx2i_conn = conn->dd_data;
int len = 0;
struct bnx2i_endpoint *bnx2i_ep = ep->dd_data;
struct bnx2i_hba *hba = bnx2i_ep->hba;
int len = -ENOTCONN;
if (!(bnx2i_conn && bnx2i_conn->ep && bnx2i_conn->ep->hba))
goto out;
if (!hba)
return -ENOTCONN;
switch (param) {
case ISCSI_PARAM_CONN_PORT:
mutex_lock(&bnx2i_conn->ep->hba->net_dev_lock);
if (bnx2i_conn->ep->cm_sk)
len = sprintf(buf, "%hu\n",
bnx2i_conn->ep->cm_sk->dst_port);
mutex_unlock(&bnx2i_conn->ep->hba->net_dev_lock);
mutex_lock(&hba->net_dev_lock);
if (bnx2i_ep->cm_sk)
len = sprintf(buf, "%hu\n", bnx2i_ep->cm_sk->dst_port);
mutex_unlock(&hba->net_dev_lock);
break;
case ISCSI_PARAM_CONN_ADDRESS:
mutex_lock(&bnx2i_conn->ep->hba->net_dev_lock);
if (bnx2i_conn->ep->cm_sk)
len = sprintf(buf, "%pI4\n",
&bnx2i_conn->ep->cm_sk->dst_ip);
mutex_unlock(&bnx2i_conn->ep->hba->net_dev_lock);
mutex_lock(&hba->net_dev_lock);
if (bnx2i_ep->cm_sk)
len = sprintf(buf, "%pI4\n", &bnx2i_ep->cm_sk->dst_ip);
mutex_unlock(&hba->net_dev_lock);
break;
default:
return iscsi_conn_get_param(cls_conn, param, buf);
return -ENOSYS;
}
out:
return len;
}
@ -1935,13 +1936,13 @@ static int bnx2i_ep_tcp_conn_active(struct bnx2i_endpoint *bnx2i_ep)
cnic_dev_10g = 1;
switch (bnx2i_ep->state) {
case EP_STATE_CONNECT_FAILED:
case EP_STATE_CLEANUP_FAILED:
case EP_STATE_OFLD_FAILED:
case EP_STATE_DISCONN_TIMEDOUT:
ret = 0;
break;
case EP_STATE_CONNECT_START:
case EP_STATE_CONNECT_FAILED:
case EP_STATE_CONNECT_COMPL:
case EP_STATE_ULP_UPDATE_START:
case EP_STATE_ULP_UPDATE_COMPL:
@ -2167,7 +2168,8 @@ struct iscsi_transport bnx2i_iscsi_transport = {
.name = "bnx2i",
.caps = CAP_RECOVERY_L0 | CAP_HDRDGST |
CAP_MULTI_R2T | CAP_DATADGST |
CAP_DATA_PATH_OFFLOAD,
CAP_DATA_PATH_OFFLOAD |
CAP_TEXT_NEGO,
.param_mask = ISCSI_MAX_RECV_DLENGTH |
ISCSI_MAX_XMIT_DLENGTH |
ISCSI_HDRDGST_EN |
@ -2200,7 +2202,7 @@ struct iscsi_transport bnx2i_iscsi_transport = {
.bind_conn = bnx2i_conn_bind,
.destroy_conn = bnx2i_conn_destroy,
.set_param = iscsi_set_param,
.get_conn_param = bnx2i_conn_get_param,
.get_conn_param = iscsi_conn_get_param,
.get_session_param = iscsi_session_get_param,
.get_host_param = bnx2i_host_get_param,
.start_conn = bnx2i_conn_start,
@ -2209,6 +2211,7 @@ 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,

View file

@ -105,7 +105,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
/* owner and name should be set already */
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
| CAP_DATADGST | CAP_DIGEST_OFFLOAD |
CAP_PADDING_OFFLOAD,
CAP_PADDING_OFFLOAD | CAP_TEXT_NEGO,
.param_mask = ISCSI_MAX_RECV_DLENGTH | ISCSI_MAX_XMIT_DLENGTH |
ISCSI_HDRDGST_EN | ISCSI_DATADGST_EN |
ISCSI_INITIAL_R2T_EN | ISCSI_MAX_R2T |
@ -137,7 +137,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
.destroy_conn = iscsi_tcp_conn_teardown,
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_conn_stop,
.get_conn_param = cxgbi_get_conn_param,
.get_conn_param = iscsi_conn_get_param,
.set_param = cxgbi_set_conn_param,
.get_stats = cxgbi_get_conn_stats,
/* pdu xmit req from user space */
@ -152,6 +152,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
.xmit_pdu = cxgbi_conn_xmit_pdu,
.parse_pdu_itt = cxgbi_parse_pdu_itt,
/* TCP connect/disconnect */
.get_ep_param = cxgbi_get_ep_param,
.ep_connect = cxgbi_ep_connect,
.ep_poll = cxgbi_ep_poll,
.ep_disconnect = cxgbi_ep_disconnect,
@ -1108,10 +1109,11 @@ static int ddp_set_map(struct cxgbi_sock *csk, struct cxgbi_pagepod_hdr *hdr,
csk, idx, npods, gl);
for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
struct sk_buff *skb = ddp->gl_skb[idx];
struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) +
PPOD_SIZE, 0, GFP_ATOMIC);
/* hold on to the skb until we clear the ddp mapping */
skb_get(skb);
if (!skb)
return -ENOMEM;
ulp_mem_io_set_hdr(skb, pm_addr);
cxgbi_ddp_ppod_set((struct cxgbi_pagepod *)(skb->head +
@ -1136,56 +1138,20 @@ static void ddp_clear_map(struct cxgbi_hba *chba, unsigned int tag,
cdev, idx, npods, tag);
for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
struct sk_buff *skb = ddp->gl_skb[idx];
struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) +
PPOD_SIZE, 0, GFP_ATOMIC);
if (!skb) {
pr_err("tag 0x%x, 0x%x, %d/%u, skb NULL.\n",
pr_err("tag 0x%x, 0x%x, %d/%u, skb OOM.\n",
tag, idx, i, npods);
continue;
}
ddp->gl_skb[idx] = NULL;
memset(skb->head + sizeof(struct ulp_mem_io), 0, PPOD_SIZE);
ulp_mem_io_set_hdr(skb, pm_addr);
skb->priority = CPL_PRIORITY_CONTROL;
cxgb3_ofld_send(cdev->lldev, skb);
}
}
static void ddp_free_gl_skb(struct cxgbi_ddp_info *ddp, int idx, int cnt)
{
int i;
log_debug(1 << CXGBI_DBG_DDP,
"ddp 0x%p, idx %d, cnt %d.\n", ddp, idx, cnt);
for (i = 0; i < cnt; i++, idx++)
if (ddp->gl_skb[idx]) {
kfree_skb(ddp->gl_skb[idx]);
ddp->gl_skb[idx] = NULL;
}
}
static int ddp_alloc_gl_skb(struct cxgbi_ddp_info *ddp, int idx,
int cnt, gfp_t gfp)
{
int i;
log_debug(1 << CXGBI_DBG_DDP,
"ddp 0x%p, idx %d, cnt %d.\n", ddp, idx, cnt);
for (i = 0; i < cnt; i++) {
struct sk_buff *skb = alloc_wr(sizeof(struct ulp_mem_io) +
PPOD_SIZE, 0, gfp);
if (skb)
ddp->gl_skb[idx + i] = skb;
else {
ddp_free_gl_skb(ddp, idx, i);
return -ENOMEM;
}
}
return 0;
}
static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk,
unsigned int tid, int pg_idx, bool reply)
{
@ -1316,8 +1282,6 @@ static int cxgb3i_ddp_init(struct cxgbi_device *cdev)
}
tdev->ulp_iscsi = ddp;
cdev->csk_ddp_free_gl_skb = ddp_free_gl_skb;
cdev->csk_ddp_alloc_gl_skb = ddp_alloc_gl_skb;
cdev->csk_ddp_setup_digest = ddp_setup_conn_digest;
cdev->csk_ddp_setup_pgidx = ddp_setup_conn_pgidx;
cdev->csk_ddp_set = ddp_set_map;

View file

@ -24,10 +24,21 @@
extern cxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS];
#define cxgb3i_get_private_ipv4addr(ndev) \
(((struct port_info *)(netdev_priv(ndev)))->iscsi_ipv4addr)
#define cxgb3i_set_private_ipv4addr(ndev, addr) \
(((struct port_info *)(netdev_priv(ndev)))->iscsi_ipv4addr) = addr
static inline unsigned int cxgb3i_get_private_ipv4addr(struct net_device *ndev)
{
return ((struct port_info *)(netdev_priv(ndev)))->iscsi_ipv4addr;
}
static inline void cxgb3i_set_private_ipv4addr(struct net_device *ndev,
unsigned int addr)
{
struct port_info *pi = (struct port_info *)netdev_priv(ndev);
pi->iscsic.flags = addr ? 1 : 0;
pi->iscsi_ipv4addr = addr;
if (addr)
memcpy(pi->iscsic.mac_addr, ndev->dev_addr, ETH_ALEN);
}
struct cpl_iscsi_hdr_norss {
union opcode_tid ot;

View file

@ -106,7 +106,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
.name = DRV_MODULE_NAME,
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST |
CAP_DATADGST | CAP_DIGEST_OFFLOAD |
CAP_PADDING_OFFLOAD,
CAP_PADDING_OFFLOAD | CAP_TEXT_NEGO,
.param_mask = ISCSI_MAX_RECV_DLENGTH | ISCSI_MAX_XMIT_DLENGTH |
ISCSI_HDRDGST_EN | ISCSI_DATADGST_EN |
ISCSI_INITIAL_R2T_EN | ISCSI_MAX_R2T |
@ -138,7 +138,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
.destroy_conn = iscsi_tcp_conn_teardown,
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_conn_stop,
.get_conn_param = cxgbi_get_conn_param,
.get_conn_param = iscsi_conn_get_param,
.set_param = cxgbi_set_conn_param,
.get_stats = cxgbi_get_conn_stats,
/* pdu xmit req from user space */
@ -153,6 +153,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
.xmit_pdu = cxgbi_conn_xmit_pdu,
.parse_pdu_itt = cxgbi_parse_pdu_itt,
/* TCP connect/disconnect */
.get_ep_param = cxgbi_get_ep_param,
.ep_connect = cxgbi_ep_connect,
.ep_poll = cxgbi_ep_poll,
.ep_disconnect = cxgbi_ep_disconnect,
@ -1425,8 +1426,6 @@ static int cxgb4i_ddp_init(struct cxgbi_device *cdev)
cxgbi_ddp_page_size_factor(pgsz_factor);
cxgb4_iscsi_init(lldi->ports[0], tagmask, pgsz_factor);
cdev->csk_ddp_free_gl_skb = NULL;
cdev->csk_ddp_alloc_gl_skb = NULL;
cdev->csk_ddp_setup_digest = ddp_setup_conn_digest;
cdev->csk_ddp_setup_pgidx = ddp_setup_conn_pgidx;
cdev->csk_ddp_set = ddp_set_map;

View file

@ -530,6 +530,7 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr)
csk->dst = dst;
csk->daddr.sin_addr.s_addr = daddr->sin_addr.s_addr;
csk->daddr.sin_port = daddr->sin_port;
csk->daddr.sin_family = daddr->sin_family;
csk->saddr.sin_addr.s_addr = rt->rt_src;
return csk;
@ -1264,12 +1265,6 @@ static int ddp_tag_reserve(struct cxgbi_sock *csk, unsigned int tid,
return idx;
}
if (cdev->csk_ddp_alloc_gl_skb) {
err = cdev->csk_ddp_alloc_gl_skb(ddp, idx, npods, gfp);
if (err < 0)
goto unmark_entries;
}
tag = cxgbi_ddp_tag_base(tformat, sw_tag);
tag |= idx << PPOD_IDX_SHIFT;
@ -1280,11 +1275,8 @@ static int ddp_tag_reserve(struct cxgbi_sock *csk, unsigned int tid,
hdr.page_offset = htonl(gl->offset);
err = cdev->csk_ddp_set(csk, &hdr, idx, npods, gl);
if (err < 0) {
if (cdev->csk_ddp_free_gl_skb)
cdev->csk_ddp_free_gl_skb(ddp, idx, npods);
if (err < 0)
goto unmark_entries;
}
ddp->idx_last = idx;
log_debug(1 << CXGBI_DBG_DDP,
@ -1350,8 +1342,6 @@ static void ddp_destroy(struct kref *kref)
>> PPOD_PAGES_SHIFT;
pr_info("cdev 0x%p, ddp %d + %d.\n", cdev, i, npods);
kfree(gl);
if (cdev->csk_ddp_free_gl_skb)
cdev->csk_ddp_free_gl_skb(ddp, i, npods);
i += npods;
} else
i++;
@ -1394,8 +1384,6 @@ int cxgbi_ddp_init(struct cxgbi_device *cdev,
return -ENOMEM;
}
ddp->gl_map = (struct cxgbi_gather_list **)(ddp + 1);
ddp->gl_skb = (struct sk_buff **)(((char *)ddp->gl_map) +
ppmax * sizeof(struct cxgbi_gather_list *));
cdev->ddp = ddp;
spin_lock_init(&ddp->map_lock);
@ -1895,13 +1883,16 @@ EXPORT_SYMBOL_GPL(cxgbi_conn_alloc_pdu);
static inline void tx_skb_setmode(struct sk_buff *skb, int hcrc, int dcrc)
{
u8 submode = 0;
if (hcrc || dcrc) {
u8 submode = 0;
if (hcrc)
submode |= 1;
if (dcrc)
submode |= 2;
cxgbi_skcb_ulp_mode(skb) = (ULP2_MODE_ISCSI << 4) | submode;
if (hcrc)
submode |= 1;
if (dcrc)
submode |= 2;
cxgbi_skcb_ulp_mode(skb) = (ULP2_MODE_ISCSI << 4) | submode;
} else
cxgbi_skcb_ulp_mode(skb) = 0;
}
int cxgbi_conn_init_pdu(struct iscsi_task *task, unsigned int offset,
@ -2197,32 +2188,34 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn,
}
EXPORT_SYMBOL_GPL(cxgbi_set_conn_param);
int cxgbi_get_conn_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param,
char *buf)
{
struct iscsi_conn *iconn = cls_conn->dd_data;
struct cxgbi_endpoint *cep = ep->dd_data;
struct cxgbi_sock *csk;
int len;
log_debug(1 << CXGBI_DBG_ISCSI,
"cls_conn 0x%p, param %d.\n", cls_conn, param);
"cls_conn 0x%p, param %d.\n", ep, param);
switch (param) {
case ISCSI_PARAM_CONN_PORT:
spin_lock_bh(&iconn->session->lock);
len = sprintf(buf, "%hu\n", iconn->portal_port);
spin_unlock_bh(&iconn->session->lock);
break;
case ISCSI_PARAM_CONN_ADDRESS:
spin_lock_bh(&iconn->session->lock);
len = sprintf(buf, "%s\n", iconn->portal_address);
spin_unlock_bh(&iconn->session->lock);
break;
if (!cep)
return -ENOTCONN;
csk = cep->csk;
if (!csk)
return -ENOTCONN;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
&csk->daddr, param, buf);
default:
return iscsi_conn_get_param(cls_conn, param, buf);
return -ENOSYS;
}
return len;
}
EXPORT_SYMBOL_GPL(cxgbi_get_conn_param);
EXPORT_SYMBOL_GPL(cxgbi_get_ep_param);
struct iscsi_cls_conn *
cxgbi_create_conn(struct iscsi_cls_session *cls_session, u32 cid)
@ -2289,11 +2282,6 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session,
cxgbi_conn_max_xmit_dlength(conn);
cxgbi_conn_max_recv_dlength(conn);
spin_lock_bh(&conn->session->lock);
sprintf(conn->portal_address, "%pI4", &csk->daddr.sin_addr.s_addr);
conn->portal_port = ntohs(csk->daddr.sin_port);
spin_unlock_bh(&conn->session->lock);
log_debug(1 << CXGBI_DBG_ISCSI,
"cls 0x%p,0x%p, ep 0x%p, cconn 0x%p, csk 0x%p.\n",
cls_session, cls_conn, ep, cconn, csk);

View file

@ -131,7 +131,6 @@ struct cxgbi_ddp_info {
unsigned int rsvd_tag_mask;
spinlock_t map_lock;
struct cxgbi_gather_list **gl_map;
struct sk_buff **gl_skb;
};
#define DDP_PGIDX_MAX 4
@ -536,8 +535,6 @@ struct cxgbi_device {
struct cxgbi_ddp_info *ddp;
void (*dev_ddp_cleanup)(struct cxgbi_device *);
void (*csk_ddp_free_gl_skb)(struct cxgbi_ddp_info *, int, int);
int (*csk_ddp_alloc_gl_skb)(struct cxgbi_ddp_info *, int, int, gfp_t);
int (*csk_ddp_set)(struct cxgbi_sock *, struct cxgbi_pagepod_hdr *,
unsigned int, unsigned int,
struct cxgbi_gather_list *);
@ -715,7 +712,7 @@ void cxgbi_cleanup_task(struct iscsi_task *task);
void cxgbi_get_conn_stats(struct iscsi_cls_conn *, struct iscsi_stats *);
int cxgbi_set_conn_param(struct iscsi_cls_conn *,
enum iscsi_param, char *, int);
int cxgbi_get_conn_param(struct iscsi_cls_conn *, enum iscsi_param, char *);
int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param, char *);
struct iscsi_cls_conn *cxgbi_create_conn(struct iscsi_cls_session *, u32);
int cxgbi_bind_conn(struct iscsi_cls_session *,
struct iscsi_cls_conn *, u64, int);

View file

@ -25,16 +25,9 @@
#include <scsi/scsi_dh.h>
#include "../scsi_priv.h"
struct scsi_dh_devinfo_list {
struct list_head node;
char vendor[9];
char model[17];
struct scsi_device_handler *handler;
};
static DEFINE_SPINLOCK(list_lock);
static LIST_HEAD(scsi_dh_list);
static LIST_HEAD(scsi_dh_dev_list);
static int scsi_dh_list_idx = 1;
static struct scsi_device_handler *get_device_handler(const char *name)
{
@ -51,40 +44,18 @@ static struct scsi_device_handler *get_device_handler(const char *name)
return found;
}
static struct scsi_device_handler *
scsi_dh_cache_lookup(struct scsi_device *sdev)
static struct scsi_device_handler *get_device_handler_by_idx(int idx)
{
struct scsi_dh_devinfo_list *tmp;
struct scsi_device_handler *found_dh = NULL;
struct scsi_device_handler *tmp, *found = NULL;
spin_lock(&list_lock);
list_for_each_entry(tmp, &scsi_dh_dev_list, node) {
if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) &&
!strncmp(sdev->model, tmp->model, strlen(tmp->model))) {
found_dh = tmp->handler;
list_for_each_entry(tmp, &scsi_dh_list, list) {
if (tmp->idx == idx) {
found = tmp;
break;
}
}
spin_unlock(&list_lock);
return found_dh;
}
static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh,
struct scsi_device *sdev)
{
int i, found = 0;
for(i = 0; scsi_dh->devlist[i].vendor; i++) {
if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor,
strlen(scsi_dh->devlist[i].vendor)) &&
!strncmp(sdev->model, scsi_dh->devlist[i].model,
strlen(scsi_dh->devlist[i].model))) {
found = 1;
break;
}
}
return found;
}
@ -102,41 +73,14 @@ device_handler_match(struct scsi_device_handler *scsi_dh,
struct scsi_device *sdev)
{
struct scsi_device_handler *found_dh = NULL;
struct scsi_dh_devinfo_list *tmp;
int idx;
found_dh = scsi_dh_cache_lookup(sdev);
if (found_dh)
return found_dh;
idx = scsi_get_device_flags_keyed(sdev, sdev->vendor, sdev->model,
SCSI_DEVINFO_DH);
found_dh = get_device_handler_by_idx(idx);
if (scsi_dh) {
if (scsi_dh_handler_lookup(scsi_dh, sdev))
found_dh = scsi_dh;
} else {
struct scsi_device_handler *tmp_dh;
spin_lock(&list_lock);
list_for_each_entry(tmp_dh, &scsi_dh_list, list) {
if (scsi_dh_handler_lookup(tmp_dh, sdev))
found_dh = tmp_dh;
}
spin_unlock(&list_lock);
}
if (found_dh) { /* If device is found, add it to the cache */
tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
if (tmp) {
strncpy(tmp->vendor, sdev->vendor, 8);
strncpy(tmp->model, sdev->model, 16);
tmp->vendor[8] = '\0';
tmp->model[16] = '\0';
tmp->handler = found_dh;
spin_lock(&list_lock);
list_add(&tmp->node, &scsi_dh_dev_list);
spin_unlock(&list_lock);
} else {
found_dh = NULL;
}
}
if (scsi_dh && found_dh != scsi_dh)
found_dh = NULL;
return found_dh;
}
@ -373,12 +317,25 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data)
*/
int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
{
int i;
if (get_device_handler(scsi_dh->name))
return -EBUSY;
spin_lock(&list_lock);
scsi_dh->idx = scsi_dh_list_idx++;
list_add(&scsi_dh->list, &scsi_dh_list);
spin_unlock(&list_lock);
for (i = 0; scsi_dh->devlist[i].vendor; i++) {
scsi_dev_info_list_add_keyed(0,
scsi_dh->devlist[i].vendor,
scsi_dh->devlist[i].model,
NULL,
scsi_dh->idx,
SCSI_DEVINFO_DH);
}
bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
@ -395,7 +352,7 @@ EXPORT_SYMBOL_GPL(scsi_register_device_handler);
*/
int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
{
struct scsi_dh_devinfo_list *tmp, *pos;
int i;
if (!get_device_handler(scsi_dh->name))
return -ENODEV;
@ -403,14 +360,14 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
scsi_dh_notifier_remove);
for (i = 0; scsi_dh->devlist[i].vendor; i++) {
scsi_dev_info_list_del_keyed(scsi_dh->devlist[i].vendor,
scsi_dh->devlist[i].model,
SCSI_DEVINFO_DH);
}
spin_lock(&list_lock);
list_del(&scsi_dh->list);
list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) {
if (pos->handler == scsi_dh) {
list_del(&pos->node);
kfree(pos);
}
}
spin_unlock(&list_lock);
printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
@ -576,6 +533,10 @@ static int __init scsi_dh_init(void)
{
int r;
r = scsi_dev_info_add_list(SCSI_DEVINFO_DH, "SCSI Device Handler");
if (r)
return r;
r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
if (!r)
@ -590,6 +551,7 @@ static void __exit scsi_dh_exit(void)
bus_for_each_dev(&scsi_bus_type, NULL, NULL,
scsi_dh_sysfs_attr_remove);
bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
scsi_dev_info_remove_list(SCSI_DEVINFO_DH);
}
module_init(scsi_dh_init);

View file

@ -253,13 +253,15 @@ static void stpg_endio(struct request *req, int error)
{
struct alua_dh_data *h = req->end_io_data;
struct scsi_sense_hdr sense_hdr;
unsigned err = SCSI_DH_IO;
unsigned err = SCSI_DH_OK;
if (error || host_byte(req->errors) != DID_OK ||
msg_byte(req->errors) != COMMAND_COMPLETE)
msg_byte(req->errors) != COMMAND_COMPLETE) {
err = SCSI_DH_IO;
goto done;
}
if (err == SCSI_DH_IO && h->senselen > 0) {
if (h->senselen > 0) {
err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
&sense_hdr);
if (!err) {
@ -285,7 +287,8 @@ static void stpg_endio(struct request *req, int error)
print_alua_state(h->state));
}
done:
blk_put_request(req);
req->end_io_data = NULL;
__blk_put_request(req->q, req);
if (h->callback_fn) {
h->callback_fn(h->callback_data, err);
h->callback_fn = h->callback_data = NULL;
@ -303,7 +306,6 @@ done:
static unsigned submit_stpg(struct alua_dh_data *h)
{
struct request *rq;
int err = SCSI_DH_RES_TEMP_UNAVAIL;
int stpg_len = 8;
struct scsi_device *sdev = h->sdev;
@ -332,7 +334,7 @@ static unsigned submit_stpg(struct alua_dh_data *h)
rq->end_io_data = h;
blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio);
return err;
return SCSI_DH_OK;
}
/*
@ -730,7 +732,9 @@ static const struct scsi_dh_devlist alua_dev_list[] = {
{"Pillar", "Axiom" },
{"Intel", "Multi-Flex"},
{"NETAPP", "LUN"},
{"NETAPP", "LUN C-Mode"},
{"AIX", "NVDISK"},
{"Promise", "VTrak"},
{NULL, NULL}
};
@ -759,7 +763,7 @@ static int alua_bus_attach(struct scsi_device *sdev)
unsigned long flags;
int err = SCSI_DH_OK;
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
+ sizeof(*h) , GFP_KERNEL);
if (!scsi_dh_data) {
sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",

View file

@ -650,7 +650,7 @@ static int clariion_bus_attach(struct scsi_device *sdev)
unsigned long flags;
int err;
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
+ sizeof(*h) , GFP_KERNEL);
if (!scsi_dh_data) {
sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",

View file

@ -225,7 +225,8 @@ static void start_stop_endio(struct request *req, int error)
}
}
done:
blk_put_request(req);
req->end_io_data = NULL;
__blk_put_request(req->q, req);
if (h->callback_fn) {
h->callback_fn(h->callback_data, err);
h->callback_fn = h->callback_data = NULL;
@ -338,8 +339,8 @@ static int hp_sw_bus_attach(struct scsi_device *sdev)
unsigned long flags;
int ret;
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
+ sizeof(*h) , GFP_KERNEL);
if (!scsi_dh_data) {
sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n",
HP_SW_NAME);

View file

@ -281,11 +281,13 @@ static struct request *get_rdac_req(struct scsi_device *sdev,
}
static struct request *rdac_failover_get(struct scsi_device *sdev,
struct rdac_dh_data *h)
struct rdac_dh_data *h, struct list_head *list)
{
struct request *rq;
struct rdac_mode_common *common;
unsigned data_size;
struct rdac_queue_data *qdata;
u8 *lun_table;
if (h->ctlr->use_ms10) {
struct rdac_pg_expanded *rdac_pg;
@ -298,6 +300,7 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
rdac_pg->subpage_code = 0x1;
rdac_pg->page_len[0] = 0x01;
rdac_pg->page_len[1] = 0x28;
lun_table = rdac_pg->lun_table;
} else {
struct rdac_pg_legacy *rdac_pg;
@ -307,11 +310,16 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
rdac_pg->page_len = 0x68;
lun_table = rdac_pg->lun_table;
}
common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
common->rdac_options = RDAC_FORCED_QUIESENCE;
list_for_each_entry(qdata, list, entry) {
lun_table[qdata->h->lun] = 0x81;
}
/* get request for block layer packet command */
rq = get_rdac_req(sdev, &h->ctlr->mode_select, data_size, WRITE);
if (!rq)
@ -565,7 +573,6 @@ static void send_mode_select(struct work_struct *work)
int err, retry_cnt = RDAC_RETRY_COUNT;
struct rdac_queue_data *tmp, *qdata;
LIST_HEAD(list);
u8 *lun_table;
spin_lock(&ctlr->ms_lock);
list_splice_init(&ctlr->ms_head, &list);
@ -573,21 +580,12 @@ static void send_mode_select(struct work_struct *work)
ctlr->ms_sdev = NULL;
spin_unlock(&ctlr->ms_lock);
if (ctlr->use_ms10)
lun_table = ctlr->mode_select.expanded.lun_table;
else
lun_table = ctlr->mode_select.legacy.lun_table;
retry:
err = SCSI_DH_RES_TEMP_UNAVAIL;
rq = rdac_failover_get(sdev, h);
rq = rdac_failover_get(sdev, h, &list);
if (!rq)
goto done;
list_for_each_entry(qdata, &list, entry) {
lun_table[qdata->h->lun] = 0x81;
}
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"%s MODE_SELECT command",
(char *) h->ctlr->array_name, h->ctlr->index,
@ -769,6 +767,7 @@ static const struct scsi_dh_devlist rdac_dev_list[] = {
{"DELL", "MD32xx"},
{"DELL", "MD32xxi"},
{"DELL", "MD36xxi"},
{"DELL", "MD36xxf"},
{"LSI", "INF-01-00"},
{"ENGENIO", "INF-01-00"},
{"STK", "FLEXLINE 380"},
@ -800,7 +799,7 @@ static int rdac_bus_attach(struct scsi_device *sdev)
int err;
char array_name[ARRAY_LABEL_LEN];
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
+ sizeof(*h) , GFP_KERNEL);
if (!scsi_dh_data) {
sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
@ -906,4 +905,5 @@ module_exit(rdac_exit);
MODULE_DESCRIPTION("Multipath LSI/Engenio RDAC driver");
MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
MODULE_VERSION("01.00.0000.0000");
MODULE_LICENSE("GPL");

View file

@ -1,2 +1,4 @@
obj-$(CONFIG_FCOE) += fcoe.o
obj-$(CONFIG_LIBFCOE) += libfcoe.o
libfcoe-objs := fcoe_ctlr.o fcoe_transport.o

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,7 @@
#include <linux/kthread.h>
#define FCOE_MAX_QUEUE_DEPTH 256
#define FCOE_LOW_QUEUE_DEPTH 32
#define FCOE_MIN_QUEUE_DEPTH 32
#define FCOE_WORD_TO_BYTE 4
@ -40,12 +40,6 @@
#define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */
#define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */
/*
* Max MTU for FCoE: 14 (FCoE header) + 24 (FC header) + 2112 (max FC payload)
* + 4 (FC CRC) + 4 (FCoE trailer) = 2158 bytes
*/
#define FCOE_MTU 2158
unsigned int fcoe_debug_logging;
module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
@ -70,21 +64,6 @@ do { \
printk(KERN_INFO "fcoe: %s: " fmt, \
netdev->name, ##args);)
/**
* struct fcoe_percpu_s - The per-CPU context for FCoE receive threads
* @thread: The thread context
* @fcoe_rx_list: The queue of pending packets to process
* @page: The memory page for calculating frame trailer CRCs
* @crc_eof_offset: The offset into the CRC page pointing to available
* memory for a new trailer
*/
struct fcoe_percpu_s {
struct task_struct *thread;
struct sk_buff_head fcoe_rx_list;
struct page *crc_eof_page;
int crc_eof_offset;
};
/**
* struct fcoe_interface - A FCoE interface
* @list: Handle for a list of FCoE interfaces
@ -108,30 +87,6 @@ struct fcoe_interface {
struct kref kref;
};
/**
* struct fcoe_port - The FCoE private structure
* @fcoe: The associated fcoe interface
* @lport: The associated local port
* @fcoe_pending_queue: The pending Rx queue of skbs
* @fcoe_pending_queue_active: Indicates if the pending queue is active
* @timer: The queue timer
* @destroy_work: Handle for work context
* (to prevent RTNL deadlocks)
* @data_srt_addr: Source address for data
*
* An instance of this structure is to be allocated along with the
* Scsi_Host and libfc fc_lport structures.
*/
struct fcoe_port {
struct fcoe_interface *fcoe;
struct fc_lport *lport;
struct sk_buff_head fcoe_pending_queue;
u8 fcoe_pending_queue_active;
struct timer_list timer;
struct work_struct destroy_work;
u8 data_src_addr[ETH_ALEN];
};
#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
/**
@ -140,7 +95,8 @@ struct fcoe_port {
*/
static inline struct net_device *fcoe_netdev(const struct fc_lport *lport)
{
return ((struct fcoe_port *)lport_priv(lport))->fcoe->netdev;
return ((struct fcoe_interface *)
((struct fcoe_port *)lport_priv(lport))->priv)->netdev;
}
#endif /* _FCOE_H_ */

View file

@ -44,9 +44,7 @@
#include <scsi/libfc.h>
#include <scsi/libfcoe.h>
MODULE_AUTHOR("Open-FCoE.org");
MODULE_DESCRIPTION("FIP discovery protocol support for FCoE HBAs");
MODULE_LICENSE("GPL v2");
#include "libfcoe.h"
#define FCOE_CTLR_MIN_FKA 500 /* min keep alive (mS) */
#define FCOE_CTLR_DEF_FKA FIP_DEF_FKA /* default keep alive (mS) */
@ -66,31 +64,7 @@ static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS;
static u8 fcoe_all_p2p[ETH_ALEN] = FIP_ALL_P2P_MACS;
unsigned int libfcoe_debug_logging;
module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */
#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \
do { \
if (unlikely(libfcoe_debug_logging & LEVEL)) \
do { \
CMD; \
} while (0); \
} while (0)
#define LIBFCOE_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \
printk(KERN_INFO "libfcoe: " fmt, ##args);)
#define LIBFCOE_FIP_DBG(fip, fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \
printk(KERN_INFO "host%d: fip: " fmt, \
(fip)->lp->host->host_no, ##args);)
static const char *fcoe_ctlr_states[] = {
static const char * const fcoe_ctlr_states[] = {
[FIP_ST_DISABLED] = "DISABLED",
[FIP_ST_LINK_WAIT] = "LINK_WAIT",
[FIP_ST_AUTO] = "AUTO",
@ -308,8 +282,8 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
struct fip_mac_desc mac;
struct fip_wwn_desc wwnn;
struct fip_size_desc size;
} __attribute__((packed)) desc;
} __attribute__((packed)) *sol;
} __packed desc;
} __packed * sol;
u32 fcoe_size;
skb = dev_alloc_skb(sizeof(*sol));
@ -456,7 +430,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
struct ethhdr eth;
struct fip_header fip;
struct fip_mac_desc mac;
} __attribute__((packed)) *kal;
} __packed * kal;
struct fip_vn_desc *vn;
u32 len;
struct fc_lport *lp;
@ -527,7 +501,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
struct ethhdr eth;
struct fip_header fip;
struct fip_encaps encaps;
} __attribute__((packed)) *cap;
} __packed * cap;
struct fc_frame_header *fh;
struct fip_mac_desc *mac;
struct fcoe_fcf *fcf;
@ -1819,7 +1793,7 @@ static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip,
struct fip_mac_desc mac;
struct fip_wwn_desc wwnn;
struct fip_vn_desc vn;
} __attribute__((packed)) *frame;
} __packed * frame;
struct fip_fc4_feat *ff;
struct fip_size_desc *size;
u32 fcp_feat;

View file

@ -0,0 +1,770 @@
/*
* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Maintained at www.Open-FCoE.org
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/errno.h>
#include <linux/crc32.h>
#include <scsi/libfcoe.h>
#include "libfcoe.h"
MODULE_AUTHOR("Open-FCoE.org");
MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs");
MODULE_LICENSE("GPL v2");
static int fcoe_transport_create(const char *, struct kernel_param *);
static int fcoe_transport_destroy(const char *, struct kernel_param *);
static int fcoe_transport_show(char *buffer, const struct kernel_param *kp);
static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device);
static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device);
static int fcoe_transport_enable(const char *, struct kernel_param *);
static int fcoe_transport_disable(const char *, struct kernel_param *);
static int libfcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr);
static LIST_HEAD(fcoe_transports);
static DEFINE_MUTEX(ft_mutex);
static LIST_HEAD(fcoe_netdevs);
static DEFINE_MUTEX(fn_mutex);
unsigned int libfcoe_debug_logging;
module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
module_param_call(show, NULL, fcoe_transport_show, NULL, S_IRUSR);
__MODULE_PARM_TYPE(show, "string");
MODULE_PARM_DESC(show, " Show attached FCoE transports");
module_param_call(create, fcoe_transport_create, NULL,
(void *)FIP_MODE_FABRIC, S_IWUSR);
__MODULE_PARM_TYPE(create, "string");
MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface");
module_param_call(create_vn2vn, fcoe_transport_create, NULL,
(void *)FIP_MODE_VN2VN, S_IWUSR);
__MODULE_PARM_TYPE(create_vn2vn, "string");
MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance "
"on an Ethernet interface");
module_param_call(destroy, fcoe_transport_destroy, NULL, NULL, S_IWUSR);
__MODULE_PARM_TYPE(destroy, "string");
MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface");
module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR);
__MODULE_PARM_TYPE(enable, "string");
MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface.");
module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR);
__MODULE_PARM_TYPE(disable, "string");
MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface.");
/* notification function for packets from net device */
static struct notifier_block libfcoe_notifier = {
.notifier_call = libfcoe_device_notification,
};
/**
* fcoe_fc_crc() - Calculates the CRC for a given frame
* @fp: The frame to be checksumed
*
* This uses crc32() routine to calculate the CRC for a frame
*
* Return: The 32 bit CRC value
*/
u32 fcoe_fc_crc(struct fc_frame *fp)
{
struct sk_buff *skb = fp_skb(fp);
struct skb_frag_struct *frag;
unsigned char *data;
unsigned long off, len, clen;
u32 crc;
unsigned i;
crc = crc32(~0, skb->data, skb_headlen(skb));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
frag = &skb_shinfo(skb)->frags[i];
off = frag->page_offset;
len = frag->size;
while (len > 0) {
clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
KM_SKB_DATA_SOFTIRQ);
crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
off += clen;
len -= clen;
}
}
return crc;
}
EXPORT_SYMBOL_GPL(fcoe_fc_crc);
/**
* fcoe_start_io() - Start FCoE I/O
* @skb: The packet to be transmitted
*
* This routine is called from the net device to start transmitting
* FCoE packets.
*
* Returns: 0 for success
*/
int fcoe_start_io(struct sk_buff *skb)
{
struct sk_buff *nskb;
int rc;
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
return -ENOMEM;
rc = dev_queue_xmit(nskb);
if (rc != 0)
return rc;
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(fcoe_start_io);
/**
* fcoe_clean_pending_queue() - Dequeue a skb and free it
* @lport: The local port to dequeue a skb on
*/
void fcoe_clean_pending_queue(struct fc_lport *lport)
{
struct fcoe_port *port = lport_priv(lport);
struct sk_buff *skb;
spin_lock_bh(&port->fcoe_pending_queue.lock);
while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) {
spin_unlock_bh(&port->fcoe_pending_queue.lock);
kfree_skb(skb);
spin_lock_bh(&port->fcoe_pending_queue.lock);
}
spin_unlock_bh(&port->fcoe_pending_queue.lock);
}
EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
/**
* fcoe_check_wait_queue() - Attempt to clear the transmit backlog
* @lport: The local port whose backlog is to be cleared
*
* This empties the wait_queue, dequeues the head of the wait_queue queue
* and calls fcoe_start_io() for each packet. If all skb have been
* transmitted it returns the qlen. If an error occurs it restores
* wait_queue (to try again later) and returns -1.
*
* The wait_queue is used when the skb transmit fails. The failed skb
* will go in the wait_queue which will be emptied by the timer function or
* by the next skb transmit.
*/
void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
{
struct fcoe_port *port = lport_priv(lport);
int rc;
spin_lock_bh(&port->fcoe_pending_queue.lock);
if (skb)
__skb_queue_tail(&port->fcoe_pending_queue, skb);
if (port->fcoe_pending_queue_active)
goto out;
port->fcoe_pending_queue_active = 1;
while (port->fcoe_pending_queue.qlen) {
/* keep qlen > 0 until fcoe_start_io succeeds */
port->fcoe_pending_queue.qlen++;
skb = __skb_dequeue(&port->fcoe_pending_queue);
spin_unlock_bh(&port->fcoe_pending_queue.lock);
rc = fcoe_start_io(skb);
spin_lock_bh(&port->fcoe_pending_queue.lock);
if (rc) {
__skb_queue_head(&port->fcoe_pending_queue, skb);
/* undo temporary increment above */
port->fcoe_pending_queue.qlen--;
break;
}
/* undo temporary increment above */
port->fcoe_pending_queue.qlen--;
}
if (port->fcoe_pending_queue.qlen < port->min_queue_depth)
lport->qfull = 0;
if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
mod_timer(&port->timer, jiffies + 2);
port->fcoe_pending_queue_active = 0;
out:
if (port->fcoe_pending_queue.qlen > port->max_queue_depth)
lport->qfull = 1;
spin_unlock_bh(&port->fcoe_pending_queue.lock);
}
EXPORT_SYMBOL_GPL(fcoe_check_wait_queue);
/**
* fcoe_queue_timer() - The fcoe queue timer
* @lport: The local port
*
* Calls fcoe_check_wait_queue on timeout
*/
void fcoe_queue_timer(ulong lport)
{
fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
}
EXPORT_SYMBOL_GPL(fcoe_queue_timer);
/**
* fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC
* @skb: The packet to be transmitted
* @tlen: The total length of the trailer
* @fps: The fcoe context
*
* This routine allocates a page for frame trailers. The page is re-used if
* there is enough room left on it for the current trailer. If there isn't
* enough buffer left a new page is allocated for the trailer. Reference to
* the page from this function as well as the skbs using the page fragments
* ensure that the page is freed at the appropriate time.
*
* Returns: 0 for success
*/
int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen,
struct fcoe_percpu_s *fps)
{
struct page *page;
page = fps->crc_eof_page;
if (!page) {
page = alloc_page(GFP_ATOMIC);
if (!page)
return -ENOMEM;
fps->crc_eof_page = page;
fps->crc_eof_offset = 0;
}
get_page(page);
skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
fps->crc_eof_offset, tlen);
skb->len += tlen;
skb->data_len += tlen;
skb->truesize += tlen;
fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
if (fps->crc_eof_offset >= PAGE_SIZE) {
fps->crc_eof_page = NULL;
fps->crc_eof_offset = 0;
put_page(page);
}
return 0;
}
EXPORT_SYMBOL_GPL(fcoe_get_paged_crc_eof);
/**
* fcoe_transport_lookup - find an fcoe transport that matches a netdev
* @netdev: The netdev to look for from all attached transports
*
* Returns : ptr to the fcoe transport that supports this netdev or NULL
* if not found.
*
* The ft_mutex should be held when this is called
*/
static struct fcoe_transport *fcoe_transport_lookup(struct net_device *netdev)
{
struct fcoe_transport *ft = NULL;
list_for_each_entry(ft, &fcoe_transports, list)
if (ft->match && ft->match(netdev))
return ft;
return NULL;
}
/**
* fcoe_transport_attach - Attaches an FCoE transport
* @ft: The fcoe transport to be attached
*
* Returns : 0 for success
*/
int fcoe_transport_attach(struct fcoe_transport *ft)
{
int rc = 0;
mutex_lock(&ft_mutex);
if (ft->attached) {
LIBFCOE_TRANSPORT_DBG("transport %s already attached\n",
ft->name);
rc = -EEXIST;
goto out_attach;
}
/* Add default transport to the tail */
if (strcmp(ft->name, FCOE_TRANSPORT_DEFAULT))
list_add(&ft->list, &fcoe_transports);
else
list_add_tail(&ft->list, &fcoe_transports);
ft->attached = true;
LIBFCOE_TRANSPORT_DBG("attaching transport %s\n", ft->name);
out_attach:
mutex_unlock(&ft_mutex);
return rc;
}
EXPORT_SYMBOL(fcoe_transport_attach);
/**
* fcoe_transport_attach - Detaches an FCoE transport
* @ft: The fcoe transport to be attached
*
* Returns : 0 for success
*/
int fcoe_transport_detach(struct fcoe_transport *ft)
{
int rc = 0;
mutex_lock(&ft_mutex);
if (!ft->attached) {
LIBFCOE_TRANSPORT_DBG("transport %s already detached\n",
ft->name);
rc = -ENODEV;
goto out_attach;
}
list_del(&ft->list);
ft->attached = false;
LIBFCOE_TRANSPORT_DBG("detaching transport %s\n", ft->name);
out_attach:
mutex_unlock(&ft_mutex);
return rc;
}
EXPORT_SYMBOL(fcoe_transport_detach);
static int fcoe_transport_show(char *buffer, const struct kernel_param *kp)
{
int i, j;
struct fcoe_transport *ft = NULL;
i = j = sprintf(buffer, "Attached FCoE transports:");
mutex_lock(&ft_mutex);
list_for_each_entry(ft, &fcoe_transports, list) {
i += snprintf(&buffer[i], IFNAMSIZ, "%s ", ft->name);
if (i >= PAGE_SIZE)
break;
}
mutex_unlock(&ft_mutex);
if (i == j)
i += snprintf(&buffer[i], IFNAMSIZ, "none");
return i;
}
static int __init fcoe_transport_init(void)
{
register_netdevice_notifier(&libfcoe_notifier);
return 0;
}
static int __exit fcoe_transport_exit(void)
{
struct fcoe_transport *ft;
unregister_netdevice_notifier(&libfcoe_notifier);
mutex_lock(&ft_mutex);
list_for_each_entry(ft, &fcoe_transports, list)
printk(KERN_ERR "FCoE transport %s is still attached!\n",
ft->name);
mutex_unlock(&ft_mutex);
return 0;
}
static int fcoe_add_netdev_mapping(struct net_device *netdev,
struct fcoe_transport *ft)
{
struct fcoe_netdev_mapping *nm;
nm = kmalloc(sizeof(*nm), GFP_KERNEL);
if (!nm) {
printk(KERN_ERR "Unable to allocate netdev_mapping");
return -ENOMEM;
}
nm->netdev = netdev;
nm->ft = ft;
mutex_lock(&fn_mutex);
list_add(&nm->list, &fcoe_netdevs);
mutex_unlock(&fn_mutex);
return 0;
}
static void fcoe_del_netdev_mapping(struct net_device *netdev)
{
struct fcoe_netdev_mapping *nm = NULL, *tmp;
mutex_lock(&fn_mutex);
list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) {
if (nm->netdev == netdev) {
list_del(&nm->list);
kfree(nm);
mutex_unlock(&fn_mutex);
return;
}
}
mutex_unlock(&fn_mutex);
}
/**
* fcoe_netdev_map_lookup - find the fcoe transport that matches the netdev on which
* it was created
*
* Returns : ptr to the fcoe transport that supports this netdev or NULL
* if not found.
*
* The ft_mutex should be held when this is called
*/
static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *netdev)
{
struct fcoe_transport *ft = NULL;
struct fcoe_netdev_mapping *nm;
mutex_lock(&fn_mutex);
list_for_each_entry(nm, &fcoe_netdevs, list) {
if (netdev == nm->netdev) {
ft = nm->ft;
mutex_unlock(&fn_mutex);
return ft;
}
}
mutex_unlock(&fn_mutex);
return NULL;
}
/**
* fcoe_if_to_netdev() - Parse a name buffer to get a net device
* @buffer: The name of the net device
*
* Returns: NULL or a ptr to net_device
*/
static struct net_device *fcoe_if_to_netdev(const char *buffer)
{
char *cp;
char ifname[IFNAMSIZ + 2];
if (buffer) {
strlcpy(ifname, buffer, IFNAMSIZ);
cp = ifname + strlen(ifname);
while (--cp >= ifname && *cp == '\n')
*cp = '\0';
return dev_get_by_name(&init_net, ifname);
}
return NULL;
}
/**
* libfcoe_device_notification() - Handler for net device events
* @notifier: The context of the notification
* @event: The type of event
* @ptr: The net device that the event was on
*
* This function is called by the Ethernet driver in case of link change event.
*
* Returns: 0 for success
*/
static int libfcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr)
{
struct net_device *netdev = ptr;
switch (event) {
case NETDEV_UNREGISTER:
printk(KERN_ERR "libfcoe_device_notification: NETDEV_UNREGISTER %s\n",
netdev->name);
fcoe_del_netdev_mapping(netdev);
break;
}
return NOTIFY_OK;
}
/**
* fcoe_transport_create() - Create a fcoe interface
* @buffer: The name of the Ethernet interface to create on
* @kp: The associated kernel param
*
* Called from sysfs. This holds the ft_mutex while calling the
* registered fcoe transport's create function.
*
* Returns: 0 for success
*/
static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
if (!mutex_trylock(&ft_mutex))
return restart_syscall();
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
* removed. Module parameter sysfs files are writable before the
* module_init function is called and after module_exit.
*/
if (THIS_MODULE->state != MODULE_STATE_LIVE)
goto out_nodev;
#endif
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buffer);
goto out_nodev;
}
ft = fcoe_netdev_map_lookup(netdev);
if (ft) {
LIBFCOE_TRANSPORT_DBG("transport %s already has existing "
"FCoE instance on %s.\n",
ft->name, netdev->name);
rc = -EEXIST;
goto out_putdev;
}
ft = fcoe_transport_lookup(netdev);
if (!ft) {
LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n",
netdev->name);
goto out_putdev;
}
rc = fcoe_add_netdev_mapping(netdev, ft);
if (rc) {
LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping "
"for FCoE transport %s for %s.\n",
ft->name, netdev->name);
goto out_putdev;
}
/* pass to transport create */
rc = ft->create ? ft->create(netdev, fip_mode) : -ENODEV;
if (rc)
fcoe_del_netdev_mapping(netdev);
LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n",
ft->name, (rc) ? "failed" : "succeeded",
netdev->name);
out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
if (rc == -ERESTARTSYS)
return restart_syscall();
else
return rc;
}
/**
* fcoe_transport_destroy() - Destroy a FCoE interface
* @buffer: The name of the Ethernet interface to be destroyed
* @kp: The associated kernel parameter
*
* Called from sysfs. This holds the ft_mutex while calling the
* registered fcoe transport's destroy function.
*
* Returns: 0 for success
*/
static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
if (!mutex_trylock(&ft_mutex))
return restart_syscall();
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
* removed. Module parameter sysfs files are writable before the
* module_init function is called and after module_exit.
*/
if (THIS_MODULE->state != MODULE_STATE_LIVE)
goto out_nodev;
#endif
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buffer);
goto out_nodev;
}
ft = fcoe_netdev_map_lookup(netdev);
if (!ft) {
LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n",
netdev->name);
goto out_putdev;
}
/* pass to transport destroy */
rc = ft->destroy ? ft->destroy(netdev) : -ENODEV;
fcoe_del_netdev_mapping(netdev);
LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n",
ft->name, (rc) ? "failed" : "succeeded",
netdev->name);
out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
if (rc == -ERESTARTSYS)
return restart_syscall();
else
return rc;
}
/**
* fcoe_transport_disable() - Disables a FCoE interface
* @buffer: The name of the Ethernet interface to be disabled
* @kp: The associated kernel parameter
*
* Called from sysfs.
*
* Returns: 0 for success
*/
static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
if (!mutex_trylock(&ft_mutex))
return restart_syscall();
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
* removed. Module parameter sysfs files are writable before the
* module_init function is called and after module_exit.
*/
if (THIS_MODULE->state != MODULE_STATE_LIVE)
goto out_nodev;
#endif
netdev = fcoe_if_to_netdev(buffer);
if (!netdev)
goto out_nodev;
ft = fcoe_netdev_map_lookup(netdev);
if (!ft)
goto out_putdev;
rc = ft->disable ? ft->disable(netdev) : -ENODEV;
out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
if (rc == -ERESTARTSYS)
return restart_syscall();
else
return rc;
}
/**
* fcoe_transport_enable() - Enables a FCoE interface
* @buffer: The name of the Ethernet interface to be enabled
* @kp: The associated kernel parameter
*
* Called from sysfs.
*
* Returns: 0 for success
*/
static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
{
int rc = -ENODEV;
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
if (!mutex_trylock(&ft_mutex))
return restart_syscall();
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
* removed. Module parameter sysfs files are writable before the
* module_init function is called and after module_exit.
*/
if (THIS_MODULE->state != MODULE_STATE_LIVE)
goto out_nodev;
#endif
netdev = fcoe_if_to_netdev(buffer);
if (!netdev)
goto out_nodev;
ft = fcoe_netdev_map_lookup(netdev);
if (!ft)
goto out_putdev;
rc = ft->enable ? ft->enable(netdev) : -ENODEV;
out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
if (rc == -ERESTARTSYS)
return restart_syscall();
else
return rc;
}
/**
* libfcoe_init() - Initialization routine for libfcoe.ko
*/
static int __init libfcoe_init(void)
{
fcoe_transport_init();
return 0;
}
module_init(libfcoe_init);
/**
* libfcoe_exit() - Tear down libfcoe.ko
*/
static void __exit libfcoe_exit(void)
{
fcoe_transport_exit();
}
module_exit(libfcoe_exit);

View file

@ -0,0 +1,31 @@
#ifndef _FCOE_LIBFCOE_H_
#define _FCOE_LIBFCOE_H_
extern unsigned int libfcoe_debug_logging;
#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */
#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
#define LIBFCOE_TRANSPORT_LOGGING 0x04 /* FCoE transport logging */
#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \
do { \
if (unlikely(libfcoe_debug_logging & LEVEL)) \
do { \
CMD; \
} while (0); \
} while (0)
#define LIBFCOE_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \
printk(KERN_INFO "libfcoe: " fmt, ##args);)
#define LIBFCOE_FIP_DBG(fip, fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \
printk(KERN_INFO "host%d: fip: " fmt, \
(fip)->lp->host->host_no, ##args);)
#define LIBFCOE_TRANSPORT_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_TRANSPORT_LOGGING, \
printk(KERN_INFO "%s: " fmt, \
__func__, ##args);)
#endif /* _FCOE_LIBFCOE_H_ */

View file

@ -37,7 +37,7 @@
#define DRV_NAME "fnic"
#define DRV_DESCRIPTION "Cisco FCoE HBA Driver"
#define DRV_VERSION "1.4.0.145"
#define DRV_VERSION "1.5.0.1"
#define PFX DRV_NAME ": "
#define DFX DRV_NAME "%d: "

View file

@ -654,7 +654,7 @@ void vnic_dev_unregister(struct vnic_dev *vdev)
vdev->linkstatus_pa);
if (vdev->stats)
pci_free_consistent(vdev->pdev,
sizeof(struct vnic_dev),
sizeof(struct vnic_stats),
vdev->stats, vdev->stats_pa);
if (vdev->fw_info)
pci_free_consistent(vdev->pdev,

View file

@ -74,6 +74,10 @@ static int hpsa_allow_any;
module_param(hpsa_allow_any, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(hpsa_allow_any,
"Allow hpsa driver to access unknown HP Smart Array hardware");
static int hpsa_simple_mode;
module_param(hpsa_simple_mode, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(hpsa_simple_mode,
"Use 'simple mode' rather than 'performant mode'");
/* define the PCI info for the cards we can control */
static const struct pci_device_id hpsa_pci_device_id[] = {
@ -85,11 +89,13 @@ static const struct pci_device_id hpsa_pci_device_id[] = {
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324a},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324b},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3233},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3250},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3251},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3252},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3253},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3254},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3350},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3351},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3352},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3353},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3354},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3355},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSF, 0x103C, 0x3356},
{PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
{0,}
@ -109,11 +115,13 @@ static struct board_type products[] = {
{0x3249103C, "Smart Array P812", &SA5_access},
{0x324a103C, "Smart Array P712m", &SA5_access},
{0x324b103C, "Smart Array P711m", &SA5_access},
{0x3250103C, "Smart Array", &SA5_access},
{0x3250113C, "Smart Array", &SA5_access},
{0x3250123C, "Smart Array", &SA5_access},
{0x3250133C, "Smart Array", &SA5_access},
{0x3250143C, "Smart Array", &SA5_access},
{0x3350103C, "Smart Array", &SA5_access},
{0x3351103C, "Smart Array", &SA5_access},
{0x3352103C, "Smart Array", &SA5_access},
{0x3353103C, "Smart Array", &SA5_access},
{0x3354103C, "Smart Array", &SA5_access},
{0x3355103C, "Smart Array", &SA5_access},
{0x3356103C, "Smart Array", &SA5_access},
{0xFFFF103C, "Unknown Smart Array", &SA5_access},
};
@ -147,17 +155,7 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
static int hpsa_slave_alloc(struct scsi_device *sdev);
static void hpsa_slave_destroy(struct scsi_device *sdev);
static ssize_t raid_level_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t lunid_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t unique_id_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t host_show_firmware_revision(struct device *dev,
struct device_attribute *attr, char *buf);
static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno);
static ssize_t host_store_rescan(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static int check_for_unit_attention(struct ctlr_info *h,
struct CommandList *c);
static void check_ioctl_unit_attention(struct ctlr_info *h,
@ -173,47 +171,10 @@ static int __devinit hpsa_find_cfg_addrs(struct pci_dev *pdev,
static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
unsigned long *memory_bar);
static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id);
static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL);
static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL);
static DEVICE_ATTR(unique_id, S_IRUGO, unique_id_show, NULL);
static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
static DEVICE_ATTR(firmware_revision, S_IRUGO,
host_show_firmware_revision, NULL);
static struct device_attribute *hpsa_sdev_attrs[] = {
&dev_attr_raid_level,
&dev_attr_lunid,
&dev_attr_unique_id,
NULL,
};
static struct device_attribute *hpsa_shost_attrs[] = {
&dev_attr_rescan,
&dev_attr_firmware_revision,
NULL,
};
static struct scsi_host_template hpsa_driver_template = {
.module = THIS_MODULE,
.name = "hpsa",
.proc_name = "hpsa",
.queuecommand = hpsa_scsi_queue_command,
.scan_start = hpsa_scan_start,
.scan_finished = hpsa_scan_finished,
.change_queue_depth = hpsa_change_queue_depth,
.this_id = -1,
.use_clustering = ENABLE_CLUSTERING,
.eh_device_reset_handler = hpsa_eh_device_reset_handler,
.ioctl = hpsa_ioctl,
.slave_alloc = hpsa_slave_alloc,
.slave_destroy = hpsa_slave_destroy,
#ifdef CONFIG_COMPAT
.compat_ioctl = hpsa_compat_ioctl,
#endif
.sdev_attrs = hpsa_sdev_attrs,
.shost_attrs = hpsa_shost_attrs,
};
static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev,
void __iomem *vaddr, int wait_for_ready);
#define BOARD_NOT_READY 0
#define BOARD_READY 1
static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
{
@ -291,67 +252,63 @@ static ssize_t host_show_firmware_revision(struct device *dev,
fwrev[0], fwrev[1], fwrev[2], fwrev[3]);
}
/* Enqueuing and dequeuing functions for cmdlists. */
static inline void addQ(struct hlist_head *list, struct CommandList *c)
static ssize_t host_show_commands_outstanding(struct device *dev,
struct device_attribute *attr, char *buf)
{
hlist_add_head(&c->list, list);
struct Scsi_Host *shost = class_to_shost(dev);
struct ctlr_info *h = shost_to_hba(shost);
return snprintf(buf, 20, "%d\n", h->commands_outstanding);
}
static inline u32 next_command(struct ctlr_info *h)
static ssize_t host_show_transport_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 a;
struct ctlr_info *h;
struct Scsi_Host *shost = class_to_shost(dev);
if (unlikely(h->transMethod != CFGTBL_Trans_Performant))
return h->access.command_completed(h);
if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
a = *(h->reply_pool_head); /* Next cmd in ring buffer */
(h->reply_pool_head)++;
h->commands_outstanding--;
} else {
a = FIFO_EMPTY;
}
/* Check for wraparound */
if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
h->reply_pool_head = h->reply_pool;
h->reply_pool_wraparound ^= 1;
}
return a;
h = shost_to_hba(shost);
return snprintf(buf, 20, "%s\n",
h->transMethod & CFGTBL_Trans_Performant ?
"performant" : "simple");
}
/* set_performant_mode: Modify the tag for cciss performant
* set bit 0 for pull model, bits 3-1 for block fetch
* register number
*/
static void set_performant_mode(struct ctlr_info *h, struct CommandList *c)
/* List of controllers which cannot be reset on kexec with reset_devices */
static u32 unresettable_controller[] = {
0x324a103C, /* Smart Array P712m */
0x324b103C, /* SmartArray P711m */
0x3223103C, /* Smart Array P800 */
0x3234103C, /* Smart Array P400 */
0x3235103C, /* Smart Array P400i */
0x3211103C, /* Smart Array E200i */
0x3212103C, /* Smart Array E200 */
0x3213103C, /* Smart Array E200i */
0x3214103C, /* Smart Array E200i */
0x3215103C, /* Smart Array E200i */
0x3237103C, /* Smart Array E500 */
0x323D103C, /* Smart Array P700m */
0x409C0E11, /* Smart Array 6400 */
0x409D0E11, /* Smart Array 6400 EM */
};
static int ctlr_is_resettable(struct ctlr_info *h)
{
if (likely(h->transMethod == CFGTBL_Trans_Performant))
c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
int i;
for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++)
if (unresettable_controller[i] == h->board_id)
return 0;
return 1;
}
static void enqueue_cmd_and_start_io(struct ctlr_info *h,
struct CommandList *c)
static ssize_t host_show_resettable(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long flags;
struct ctlr_info *h;
struct Scsi_Host *shost = class_to_shost(dev);
set_performant_mode(h, c);
spin_lock_irqsave(&h->lock, flags);
addQ(&h->reqQ, c);
h->Qdepth++;
start_io(h);
spin_unlock_irqrestore(&h->lock, flags);
}
static inline void removeQ(struct CommandList *c)
{
if (WARN_ON(hlist_unhashed(&c->list)))
return;
hlist_del_init(&c->list);
}
static inline int is_hba_lunid(unsigned char scsi3addr[])
{
return memcmp(scsi3addr, RAID_CTLR_LUNID, 8) == 0;
h = shost_to_hba(shost);
return snprintf(buf, 20, "%d\n", ctlr_is_resettable(h));
}
static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[])
@ -359,15 +316,6 @@ static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[])
return (scsi3addr[3] & 0xC0) == 0x40;
}
static inline int is_scsi_rev_5(struct ctlr_info *h)
{
if (!h->hba_inquiry_data)
return 0;
if ((h->hba_inquiry_data[2] & 0x07) == 5)
return 1;
return 0;
}
static const char *raid_label[] = { "0", "4", "1(1+0)", "5", "5+1", "ADG",
"UNKNOWN"
};
@ -459,6 +407,129 @@ static ssize_t unique_id_show(struct device *dev,
sn[12], sn[13], sn[14], sn[15]);
}
static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL);
static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL);
static DEVICE_ATTR(unique_id, S_IRUGO, unique_id_show, NULL);
static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
static DEVICE_ATTR(firmware_revision, S_IRUGO,
host_show_firmware_revision, NULL);
static DEVICE_ATTR(commands_outstanding, S_IRUGO,
host_show_commands_outstanding, NULL);
static DEVICE_ATTR(transport_mode, S_IRUGO,
host_show_transport_mode, NULL);
static DEVICE_ATTR(resettable, S_IRUGO,
host_show_resettable, NULL);
static struct device_attribute *hpsa_sdev_attrs[] = {
&dev_attr_raid_level,
&dev_attr_lunid,
&dev_attr_unique_id,
NULL,
};
static struct device_attribute *hpsa_shost_attrs[] = {
&dev_attr_rescan,
&dev_attr_firmware_revision,
&dev_attr_commands_outstanding,
&dev_attr_transport_mode,
&dev_attr_resettable,
NULL,
};
static struct scsi_host_template hpsa_driver_template = {
.module = THIS_MODULE,
.name = "hpsa",
.proc_name = "hpsa",
.queuecommand = hpsa_scsi_queue_command,
.scan_start = hpsa_scan_start,
.scan_finished = hpsa_scan_finished,
.change_queue_depth = hpsa_change_queue_depth,
.this_id = -1,
.use_clustering = ENABLE_CLUSTERING,
.eh_device_reset_handler = hpsa_eh_device_reset_handler,
.ioctl = hpsa_ioctl,
.slave_alloc = hpsa_slave_alloc,
.slave_destroy = hpsa_slave_destroy,
#ifdef CONFIG_COMPAT
.compat_ioctl = hpsa_compat_ioctl,
#endif
.sdev_attrs = hpsa_sdev_attrs,
.shost_attrs = hpsa_shost_attrs,
};
/* Enqueuing and dequeuing functions for cmdlists. */
static inline void addQ(struct list_head *list, struct CommandList *c)
{
list_add_tail(&c->list, list);
}
static inline u32 next_command(struct ctlr_info *h)
{
u32 a;
if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
return h->access.command_completed(h);
if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
a = *(h->reply_pool_head); /* Next cmd in ring buffer */
(h->reply_pool_head)++;
h->commands_outstanding--;
} else {
a = FIFO_EMPTY;
}
/* Check for wraparound */
if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
h->reply_pool_head = h->reply_pool;
h->reply_pool_wraparound ^= 1;
}
return a;
}
/* set_performant_mode: Modify the tag for cciss performant
* set bit 0 for pull model, bits 3-1 for block fetch
* register number
*/
static void set_performant_mode(struct ctlr_info *h, struct CommandList *c)
{
if (likely(h->transMethod & CFGTBL_Trans_Performant))
c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
}
static void enqueue_cmd_and_start_io(struct ctlr_info *h,
struct CommandList *c)
{
unsigned long flags;
set_performant_mode(h, c);
spin_lock_irqsave(&h->lock, flags);
addQ(&h->reqQ, c);
h->Qdepth++;
start_io(h);
spin_unlock_irqrestore(&h->lock, flags);
}
static inline void removeQ(struct CommandList *c)
{
if (WARN_ON(list_empty(&c->list)))
return;
list_del_init(&c->list);
}
static inline int is_hba_lunid(unsigned char scsi3addr[])
{
return memcmp(scsi3addr, RAID_CTLR_LUNID, 8) == 0;
}
static inline int is_scsi_rev_5(struct ctlr_info *h)
{
if (!h->hba_inquiry_data)
return 0;
if ((h->hba_inquiry_data[2] & 0x07) == 5)
return 1;
return 0;
}
static int hpsa_find_target_lun(struct ctlr_info *h,
unsigned char scsi3addr[], int bus, int *target, int *lun)
{
@ -1130,6 +1201,10 @@ static void complete_scsi_command(struct CommandList *cp,
cmd->result = DID_TIME_OUT << 16;
dev_warn(&h->pdev->dev, "cp %p timedout\n", cp);
break;
case CMD_UNABORTABLE:
cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "Command unabortable\n");
break;
default:
cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "cp %p returned unknown status %x\n",
@ -1160,7 +1235,7 @@ static int hpsa_scsi_detect(struct ctlr_info *h)
sh->sg_tablesize = h->maxsgentries;
h->scsi_host = sh;
sh->hostdata[0] = (unsigned long) h;
sh->irq = h->intr[PERF_MODE_INT];
sh->irq = h->intr[h->intr_mode];
sh->unique_id = sh->irq;
error = scsi_add_host(sh, &h->pdev->dev);
if (error)
@ -1295,6 +1370,9 @@ static void hpsa_scsi_interpret_error(struct CommandList *cp)
case CMD_TIMEOUT:
dev_warn(d, "cp %p timed out\n", cp);
break;
case CMD_UNABORTABLE:
dev_warn(d, "Command unabortable\n");
break;
default:
dev_warn(d, "cp %p returned unknown status %x\n", cp,
ei->CommandStatus);
@ -1595,6 +1673,8 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
if (lun == 0) /* if lun is 0, then obviously we have a lun 0. */
return 0;
memset(scsi3addr, 0, 8);
scsi3addr[3] = target;
if (is_hba_lunid(scsi3addr))
return 0; /* Don't add the RAID controller here. */
@ -1609,8 +1689,6 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
return 0;
}
memset(scsi3addr, 0, 8);
scsi3addr[3] = target;
if (hpsa_update_device_info(h, scsi3addr, this_device))
return 0;
(*nmsa2xxx_enclosures)++;
@ -2199,7 +2277,7 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
c->cmdindex = i;
INIT_HLIST_NODE(&c->list);
INIT_LIST_HEAD(&c->list);
c->busaddr = (u32) cmd_dma_handle;
temp64.val = (u64) err_dma_handle;
c->ErrDesc.Addr.lower = temp64.val32.lower;
@ -2237,7 +2315,7 @@ static struct CommandList *cmd_special_alloc(struct ctlr_info *h)
}
memset(c->err_info, 0, sizeof(*c->err_info));
INIT_HLIST_NODE(&c->list);
INIT_LIST_HEAD(&c->list);
c->busaddr = (u32) cmd_dma_handle;
temp64.val = (u64) err_dma_handle;
c->ErrDesc.Addr.lower = temp64.val32.lower;
@ -2267,7 +2345,7 @@ static void cmd_special_free(struct ctlr_info *h, struct CommandList *c)
pci_free_consistent(h->pdev, sizeof(*c->err_info),
c->err_info, (dma_addr_t) temp64.val);
pci_free_consistent(h->pdev, sizeof(*c),
c, (dma_addr_t) c->busaddr);
c, (dma_addr_t) (c->busaddr & DIRECT_LOOKUP_MASK));
}
#ifdef CONFIG_COMPAT
@ -2281,6 +2359,7 @@ static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg)
int err;
u32 cp;
memset(&arg64, 0, sizeof(arg64));
err = 0;
err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info,
sizeof(arg64.LUN_info));
@ -2317,6 +2396,7 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
int err;
u32 cp;
memset(&arg64, 0, sizeof(arg64));
err = 0;
err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info,
sizeof(arg64.LUN_info));
@ -2433,15 +2513,17 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
buff = kmalloc(iocommand.buf_size, GFP_KERNEL);
if (buff == NULL)
return -EFAULT;
}
if (iocommand.Request.Type.Direction == XFER_WRITE) {
/* Copy the data into the buffer we created */
if (copy_from_user(buff, iocommand.buf, iocommand.buf_size)) {
kfree(buff);
return -EFAULT;
if (iocommand.Request.Type.Direction == XFER_WRITE) {
/* Copy the data into the buffer we created */
if (copy_from_user(buff, iocommand.buf,
iocommand.buf_size)) {
kfree(buff);
return -EFAULT;
}
} else {
memset(buff, 0, iocommand.buf_size);
}
} else
memset(buff, 0, iocommand.buf_size);
}
c = cmd_special_alloc(h);
if (c == NULL) {
kfree(buff);
@ -2487,8 +2569,8 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
cmd_special_free(h, c);
return -EFAULT;
}
if (iocommand.Request.Type.Direction == XFER_READ) {
if (iocommand.Request.Type.Direction == XFER_READ &&
iocommand.buf_size > 0) {
/* Copy the data out of the buffer we created */
if (copy_to_user(iocommand.buf, buff, iocommand.buf_size)) {
kfree(buff);
@ -2581,14 +2663,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
}
c->cmd_type = CMD_IOCTL_PEND;
c->Header.ReplyQueue = 0;
if (ioc->buf_size > 0) {
c->Header.SGList = sg_used;
c->Header.SGTotal = sg_used;
} else {
c->Header.SGList = 0;
c->Header.SGTotal = 0;
}
c->Header.SGList = c->Header.SGTotal = sg_used;
memcpy(&c->Header.LUN, &ioc->LUN_info, sizeof(c->Header.LUN));
c->Header.Tag.lower = c->busaddr;
memcpy(&c->Request, &ioc->Request, sizeof(c->Request));
@ -2605,7 +2680,8 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
}
}
hpsa_scsi_do_simple_cmd_core(h, c);
hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL);
if (sg_used)
hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL);
check_ioctl_unit_attention(h, c);
/* Copy the error information out */
memcpy(&ioc->error_info, c->err_info, sizeof(ioc->error_info));
@ -2614,7 +2690,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
status = -EFAULT;
goto cleanup1;
}
if (ioc->Request.Type.Direction == XFER_READ) {
if (ioc->Request.Type.Direction == XFER_READ && ioc->buf_size > 0) {
/* Copy the data out of the buffer we created */
BYTE __user *ptr = ioc->buf;
for (i = 0; i < sg_used; i++) {
@ -2810,8 +2886,8 @@ static void start_io(struct ctlr_info *h)
{
struct CommandList *c;
while (!hlist_empty(&h->reqQ)) {
c = hlist_entry(h->reqQ.first, struct CommandList, list);
while (!list_empty(&h->reqQ)) {
c = list_entry(h->reqQ.next, struct CommandList, list);
/* can't do anything if fifo is full */
if ((h->access.fifo_full(h))) {
dev_warn(&h->pdev->dev, "fifo full\n");
@ -2867,20 +2943,22 @@ static inline void finish_cmd(struct CommandList *c, u32 raw_tag)
static inline u32 hpsa_tag_contains_index(u32 tag)
{
#define DIRECT_LOOKUP_BIT 0x10
return tag & DIRECT_LOOKUP_BIT;
}
static inline u32 hpsa_tag_to_index(u32 tag)
{
#define DIRECT_LOOKUP_SHIFT 5
return tag >> DIRECT_LOOKUP_SHIFT;
}
static inline u32 hpsa_tag_discard_error_bits(u32 tag)
static inline u32 hpsa_tag_discard_error_bits(struct ctlr_info *h, u32 tag)
{
#define HPSA_ERROR_BITS 0x03
return tag & ~HPSA_ERROR_BITS;
#define HPSA_PERF_ERROR_BITS ((1 << DIRECT_LOOKUP_SHIFT) - 1)
#define HPSA_SIMPLE_ERROR_BITS 0x03
if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
return tag & ~HPSA_SIMPLE_ERROR_BITS;
return tag & ~HPSA_PERF_ERROR_BITS;
}
/* process completion of an indexed ("direct lookup") command */
@ -2904,10 +2982,9 @@ static inline u32 process_nonindexed_cmd(struct ctlr_info *h,
{
u32 tag;
struct CommandList *c = NULL;
struct hlist_node *tmp;
tag = hpsa_tag_discard_error_bits(raw_tag);
hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
tag = hpsa_tag_discard_error_bits(h, raw_tag);
list_for_each_entry(c, &h->cmpQ, list) {
if ((c->busaddr & 0xFFFFFFE0) == (tag & 0xFFFFFFE0)) {
finish_cmd(c, raw_tag);
return next_command(h);
@ -2957,7 +3034,10 @@ static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id)
return IRQ_HANDLED;
}
/* Send a message CDB to the firmware. */
/* Send a message CDB to the firmware. Careful, this only works
* in simple mode, not performant mode due to the tag lookup.
* We only ever use this immediately after a controller reset.
*/
static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
unsigned char type)
{
@ -3023,7 +3103,7 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
for (i = 0; i < HPSA_MSG_SEND_RETRY_LIMIT; i++) {
tag = readl(vaddr + SA5_REPLY_PORT_OFFSET);
if (hpsa_tag_discard_error_bits(tag) == paddr32)
if ((tag & ~HPSA_SIMPLE_ERROR_BITS) == paddr32)
break;
msleep(HPSA_MSG_SEND_RETRY_INTERVAL_MSECS);
}
@ -3055,38 +3135,6 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
#define hpsa_soft_reset_controller(p) hpsa_message(p, 1, 0)
#define hpsa_noop(p) hpsa_message(p, 3, 0)
static __devinit int hpsa_reset_msi(struct pci_dev *pdev)
{
/* the #defines are stolen from drivers/pci/msi.h. */
#define msi_control_reg(base) (base + PCI_MSI_FLAGS)
#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
int pos;
u16 control = 0;
pos = pci_find_capability(pdev, PCI_CAP_ID_MSI);
if (pos) {
pci_read_config_word(pdev, msi_control_reg(pos), &control);
if (control & PCI_MSI_FLAGS_ENABLE) {
dev_info(&pdev->dev, "resetting MSI\n");
pci_write_config_word(pdev, msi_control_reg(pos),
control & ~PCI_MSI_FLAGS_ENABLE);
}
}
pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
if (pos) {
pci_read_config_word(pdev, msi_control_reg(pos), &control);
if (control & PCI_MSIX_FLAGS_ENABLE) {
dev_info(&pdev->dev, "resetting MSI-X\n");
pci_write_config_word(pdev, msi_control_reg(pos),
control & ~PCI_MSIX_FLAGS_ENABLE);
}
}
return 0;
}
static int hpsa_controller_hard_reset(struct pci_dev *pdev,
void * __iomem vaddr, bool use_doorbell)
{
@ -3142,17 +3190,17 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
*/
static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
{
u16 saved_config_space[32];
u64 cfg_offset;
u32 cfg_base_addr;
u64 cfg_base_addr_index;
void __iomem *vaddr;
unsigned long paddr;
u32 misc_fw_support, active_transport;
int rc, i;
int rc;
struct CfgTable __iomem *cfgtable;
bool use_doorbell;
u32 board_id;
u16 command_register;
/* For controllers as old as the P600, this is very nearly
* the same thing as
@ -3162,14 +3210,6 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
* pci_set_power_state(pci_dev, PCI_D0);
* pci_restore_state(pci_dev);
*
* but we can't use these nice canned kernel routines on
* kexec, because they also check the MSI/MSI-X state in PCI
* configuration space and do the wrong thing when it is
* set/cleared. Also, the pci_save/restore_state functions
* violate the ordering requirements for restoring the
* configuration space from the CCISS document (see the
* comment below). So we roll our own ....
*
* For controllers newer than the P600, the pci power state
* method of resetting doesn't work so we have another way
* using the doorbell register.
@ -3182,13 +3222,21 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
* likely not be happy. Just forbid resetting this conjoined mess.
* The 640x isn't really supported by hpsa anyway.
*/
hpsa_lookup_board_id(pdev, &board_id);
rc = hpsa_lookup_board_id(pdev, &board_id);
if (rc < 0) {
dev_warn(&pdev->dev, "Not resetting device.\n");
return -ENODEV;
}
if (board_id == 0x409C0E11 || board_id == 0x409D0E11)
return -ENOTSUPP;
for (i = 0; i < 32; i++)
pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
/* Save the PCI command register */
pci_read_config_word(pdev, 4, &command_register);
/* Turn the board off. This is so that later pci_restore_state()
* won't turn the board on before the rest of config space is ready.
*/
pci_disable_device(pdev);
pci_save_state(pdev);
/* find the first memory BAR, so we can find the cfg table */
rc = hpsa_pci_find_memory_BAR(pdev, &paddr);
@ -3214,46 +3262,47 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
misc_fw_support = readl(&cfgtable->misc_fw_support);
use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
/* The doorbell reset seems to cause lockups on some Smart
* Arrays (e.g. P410, P410i, maybe others). Until this is
* fixed or at least isolated, avoid the doorbell reset.
*/
use_doorbell = 0;
rc = hpsa_controller_hard_reset(pdev, vaddr, use_doorbell);
if (rc)
goto unmap_cfgtable;
/* Restore the PCI configuration space. The Open CISS
* Specification says, "Restore the PCI Configuration
* Registers, offsets 00h through 60h. It is important to
* restore the command register, 16-bits at offset 04h,
* last. Do not restore the configuration status register,
* 16-bits at offset 06h." Note that the offset is 2*i.
*/
for (i = 0; i < 32; i++) {
if (i == 2 || i == 3)
continue;
pci_write_config_word(pdev, 2*i, saved_config_space[i]);
pci_restore_state(pdev);
rc = pci_enable_device(pdev);
if (rc) {
dev_warn(&pdev->dev, "failed to enable device.\n");
goto unmap_cfgtable;
}
wmb();
pci_write_config_word(pdev, 4, saved_config_space[2]);
pci_write_config_word(pdev, 4, command_register);
/* Some devices (notably the HP Smart Array 5i Controller)
need a little pause here */
msleep(HPSA_POST_RESET_PAUSE_MSECS);
/* Wait for board to become not ready, then ready. */
dev_info(&pdev->dev, "Waiting for board to become ready.\n");
rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY);
if (rc)
dev_warn(&pdev->dev,
"failed waiting for board to become not ready\n");
rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_READY);
if (rc) {
dev_warn(&pdev->dev,
"failed waiting for board to become ready\n");
goto unmap_cfgtable;
}
dev_info(&pdev->dev, "board ready.\n");
/* Controller should be in simple mode at this point. If it's not,
* It means we're on one of those controllers which doesn't support
* the doorbell reset method and on which the PCI power management reset
* method doesn't work (P800, for example.)
* In those cases, pretend the reset worked and hope for the best.
* In those cases, don't try to proceed, as it generally doesn't work.
*/
active_transport = readl(&cfgtable->TransportActive);
if (active_transport & PERFORMANT_MODE) {
dev_warn(&pdev->dev, "Unable to successfully reset controller,"
" proceeding anyway.\n");
rc = -ENOTSUPP;
" Ignoring controller.\n");
rc = -ENODEV;
}
unmap_cfgtable:
@ -3386,7 +3435,7 @@ static void __devinit hpsa_interrupt_mode(struct ctlr_info *h)
default_int_mode:
#endif /* CONFIG_PCI_MSI */
/* if we get here we're going to use the default interrupt mode */
h->intr[PERF_MODE_INT] = h->pdev->irq;
h->intr[h->intr_mode] = h->pdev->irq;
}
static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id)
@ -3438,18 +3487,28 @@ static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
return -ENODEV;
}
static int __devinit hpsa_wait_for_board_ready(struct ctlr_info *h)
static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev,
void __iomem *vaddr, int wait_for_ready)
{
int i;
int i, iterations;
u32 scratchpad;
if (wait_for_ready)
iterations = HPSA_BOARD_READY_ITERATIONS;
else
iterations = HPSA_BOARD_NOT_READY_ITERATIONS;
for (i = 0; i < HPSA_BOARD_READY_ITERATIONS; i++) {
scratchpad = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
if (scratchpad == HPSA_FIRMWARE_READY)
return 0;
for (i = 0; i < iterations; i++) {
scratchpad = readl(vaddr + SA5_SCRATCHPAD_OFFSET);
if (wait_for_ready) {
if (scratchpad == HPSA_FIRMWARE_READY)
return 0;
} else {
if (scratchpad != HPSA_FIRMWARE_READY)
return 0;
}
msleep(HPSA_BOARD_READY_POLL_INTERVAL_MSECS);
}
dev_warn(&h->pdev->dev, "board not ready, timed out.\n");
dev_warn(&pdev->dev, "board not ready, timed out.\n");
return -ENODEV;
}
@ -3497,6 +3556,11 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h)
static void __devinit hpsa_get_max_perf_mode_cmds(struct ctlr_info *h)
{
h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
/* Limit commands in memory limited kdump scenario. */
if (reset_devices && h->max_commands > 32)
h->max_commands = 32;
if (h->max_commands < 16) {
dev_warn(&h->pdev->dev, "Controller reports "
"max supported commands of %d, an obvious lie. "
@ -3571,16 +3635,21 @@ static inline void hpsa_p600_dma_prefetch_quirk(struct ctlr_info *h)
static void __devinit hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
{
int i;
u32 doorbell_value;
unsigned long flags;
/* under certain very rare conditions, this can take awhile.
* (e.g.: hot replace a failed 144GB drive in a RAID 5 set right
* as we enter this code.)
*/
for (i = 0; i < MAX_CONFIG_WAIT; i++) {
if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq))
spin_lock_irqsave(&h->lock, flags);
doorbell_value = readl(h->vaddr + SA5_DOORBELL);
spin_unlock_irqrestore(&h->lock, flags);
if (!(doorbell_value & CFGTBL_ChangeReq))
break;
/* delay and try again */
msleep(10);
usleep_range(10000, 20000);
}
}
@ -3603,6 +3672,7 @@ static int __devinit hpsa_enter_simple_mode(struct ctlr_info *h)
"unable to get board into simple mode\n");
return -ENODEV;
}
h->transMethod = CFGTBL_Trans_Simple;
return 0;
}
@ -3641,7 +3711,7 @@ static int __devinit hpsa_pci_init(struct ctlr_info *h)
err = -ENOMEM;
goto err_out_free_res;
}
err = hpsa_wait_for_board_ready(h);
err = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY);
if (err)
goto err_out_free_res;
err = hpsa_find_cfgtables(h);
@ -3710,8 +3780,6 @@ static __devinit int hpsa_init_reset_devices(struct pci_dev *pdev)
return 0; /* just try to do the kdump anyhow. */
if (rc)
return -ENODEV;
if (hpsa_reset_msi(pdev))
return -ENODEV;
/* Now try to get the controller to respond to a no-op */
for (i = 0; i < HPSA_POST_RESET_NOOP_RETRIES; i++) {
@ -3749,8 +3817,11 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
h->pdev = pdev;
h->busy_initializing = 1;
INIT_HLIST_HEAD(&h->cmpQ);
INIT_HLIST_HEAD(&h->reqQ);
h->intr_mode = hpsa_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT;
INIT_LIST_HEAD(&h->cmpQ);
INIT_LIST_HEAD(&h->reqQ);
spin_lock_init(&h->lock);
spin_lock_init(&h->scan_lock);
rc = hpsa_pci_init(h);
if (rc != 0)
goto clean1;
@ -3777,20 +3848,20 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
h->access.set_intr_mask(h, HPSA_INTR_OFF);
if (h->msix_vector || h->msi_vector)
rc = request_irq(h->intr[PERF_MODE_INT], do_hpsa_intr_msi,
rc = request_irq(h->intr[h->intr_mode], do_hpsa_intr_msi,
IRQF_DISABLED, h->devname, h);
else
rc = request_irq(h->intr[PERF_MODE_INT], do_hpsa_intr_intx,
rc = request_irq(h->intr[h->intr_mode], do_hpsa_intr_intx,
IRQF_DISABLED, h->devname, h);
if (rc) {
dev_err(&pdev->dev, "unable to get irq %d for %s\n",
h->intr[PERF_MODE_INT], h->devname);
h->intr[h->intr_mode], h->devname);
goto clean2;
}
dev_info(&pdev->dev, "%s: <0x%x> at IRQ %d%s using DAC\n",
h->devname, pdev->device,
h->intr[PERF_MODE_INT], dac ? "" : " not");
h->intr[h->intr_mode], dac ? "" : " not");
h->cmd_pool_bits =
kmalloc(((h->nr_cmds + BITS_PER_LONG -
@ -3810,8 +3881,6 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
}
if (hpsa_allocate_sg_chain_blocks(h))
goto clean4;
spin_lock_init(&h->lock);
spin_lock_init(&h->scan_lock);
init_waitqueue_head(&h->scan_wait_queue);
h->scan_finished = 1; /* no scan currently in progress */
@ -3843,7 +3912,7 @@ clean4:
h->nr_cmds * sizeof(struct ErrorInfo),
h->errinfo_pool,
h->errinfo_pool_dhandle);
free_irq(h->intr[PERF_MODE_INT], h);
free_irq(h->intr[h->intr_mode], h);
clean2:
clean1:
h->busy_initializing = 0;
@ -3887,7 +3956,7 @@ static void hpsa_shutdown(struct pci_dev *pdev)
*/
hpsa_flush_cache(h);
h->access.set_intr_mask(h, HPSA_INTR_OFF);
free_irq(h->intr[PERF_MODE_INT], h);
free_irq(h->intr[h->intr_mode], h);
#ifdef CONFIG_PCI_MSI
if (h->msix_vector)
pci_disable_msix(h->pdev);
@ -3989,7 +4058,8 @@ static void calc_bucket_map(int bucket[], int num_buckets,
}
}
static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h)
static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h,
u32 use_short_tags)
{
int i;
unsigned long register_value;
@ -4037,7 +4107,7 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h)
writel(0, &h->transtable->RepQCtrAddrHigh32);
writel(h->reply_pool_dhandle, &h->transtable->RepQAddr0Low32);
writel(0, &h->transtable->RepQAddr0High32);
writel(CFGTBL_Trans_Performant,
writel(CFGTBL_Trans_Performant | use_short_tags,
&(h->cfgtable->HostWrite.TransportRequest));
writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
hpsa_wait_for_mode_change_ack(h);
@ -4047,12 +4117,18 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h)
" performant mode\n");
return;
}
/* Change the access methods to the performant access methods */
h->access = SA5_performant_access;
h->transMethod = CFGTBL_Trans_Performant;
}
static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
{
u32 trans_support;
if (hpsa_simple_mode)
return;
trans_support = readl(&(h->cfgtable->TransportSupport));
if (!(trans_support & PERFORMANT_MODE))
return;
@ -4072,11 +4148,8 @@ static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
|| (h->blockFetchTable == NULL))
goto clean_up;
hpsa_enter_performant_mode(h);
/* Change the access methods to the performant access methods */
h->access = SA5_performant_access;
h->transMethod = CFGTBL_Trans_Performant;
hpsa_enter_performant_mode(h,
trans_support & CFGTBL_Trans_use_short_tags);
return;

View file

@ -72,11 +72,12 @@ struct ctlr_info {
unsigned int intr[4];
unsigned int msix_vector;
unsigned int msi_vector;
int intr_mode; /* either PERF_MODE_INT or SIMPLE_MODE_INT */
struct access_method access;
/* queue and queue Info */
struct hlist_head reqQ;
struct hlist_head cmpQ;
struct list_head reqQ;
struct list_head cmpQ;
unsigned int Qdepth;
unsigned int maxQsinceinit;
unsigned int maxSG;
@ -154,12 +155,16 @@ struct ctlr_info {
* HPSA_BOARD_READY_ITERATIONS are derived from those.
*/
#define HPSA_BOARD_READY_WAIT_SECS (120)
#define HPSA_BOARD_NOT_READY_WAIT_SECS (10)
#define HPSA_BOARD_READY_POLL_INTERVAL_MSECS (100)
#define HPSA_BOARD_READY_POLL_INTERVAL \
((HPSA_BOARD_READY_POLL_INTERVAL_MSECS * HZ) / 1000)
#define HPSA_BOARD_READY_ITERATIONS \
((HPSA_BOARD_READY_WAIT_SECS * 1000) / \
HPSA_BOARD_READY_POLL_INTERVAL_MSECS)
#define HPSA_BOARD_NOT_READY_ITERATIONS \
((HPSA_BOARD_NOT_READY_WAIT_SECS * 1000) / \
HPSA_BOARD_READY_POLL_INTERVAL_MSECS)
#define HPSA_POST_RESET_PAUSE_MSECS (3000)
#define HPSA_POST_RESET_NOOP_RETRIES (12)

View file

@ -104,6 +104,7 @@
#define CFGTBL_Trans_Simple 0x00000002l
#define CFGTBL_Trans_Performant 0x00000004l
#define CFGTBL_Trans_use_short_tags 0x20000000l
#define CFGTBL_BusType_Ultra2 0x00000001l
#define CFGTBL_BusType_Ultra3 0x00000002l
@ -265,6 +266,7 @@ struct ErrorInfo {
#define DIRECT_LOOKUP_SHIFT 5
#define DIRECT_LOOKUP_BIT 0x10
#define DIRECT_LOOKUP_MASK (~((1 << DIRECT_LOOKUP_SHIFT) - 1))
#define HPSA_ERROR_BIT 0x02
struct ctlr_info; /* defined in hpsa.h */
@ -291,7 +293,7 @@ struct CommandList {
struct ctlr_info *h;
int cmd_type;
long cmdindex;
struct hlist_node list;
struct list_head list;
struct request *rq;
struct completion *waiting;
void *scsi_cmd;

View file

@ -1301,7 +1301,7 @@ static void ipr_handle_config_change(struct ipr_ioa_cfg *ioa_cfg,
ipr_clear_res_target(res);
list_move_tail(&res->queue, &ioa_cfg->free_res_q);
}
} else if (!res->sdev) {
} else if (!res->sdev || res->del_from_ml) {
res->add_to_ml = 1;
if (ioa_cfg->allow_ml_add_del)
schedule_work(&ioa_cfg->work_q);
@ -3104,7 +3104,10 @@ restart:
did_work = 1;
sdev = res->sdev;
if (!scsi_device_get(sdev)) {
list_move_tail(&res->queue, &ioa_cfg->free_res_q);
if (!res->add_to_ml)
list_move_tail(&res->queue, &ioa_cfg->free_res_q);
else
res->del_from_ml = 0;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
scsi_remove_device(sdev);
scsi_device_put(sdev);
@ -8864,7 +8867,7 @@ static void __ipr_remove(struct pci_dev *pdev)
spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
flush_scheduled_work();
flush_work_sync(&ioa_cfg->work_q);
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
spin_lock(&ipr_driver_lock);

View file

@ -608,54 +608,12 @@ static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
iscsi_sw_tcp_release_conn(conn);
}
static int iscsi_sw_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
char *buf, int *port,
int (*getname)(struct socket *,
struct sockaddr *,
int *addrlen))
{
struct sockaddr_storage *addr;
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
int rc = 0, len;
addr = kmalloc(sizeof(*addr), GFP_KERNEL);
if (!addr)
return -ENOMEM;
if (getname(sock, (struct sockaddr *) addr, &len)) {
rc = -ENODEV;
goto free_addr;
}
switch (addr->ss_family) {
case AF_INET:
sin = (struct sockaddr_in *)addr;
spin_lock_bh(&conn->session->lock);
sprintf(buf, "%pI4", &sin->sin_addr.s_addr);
*port = be16_to_cpu(sin->sin_port);
spin_unlock_bh(&conn->session->lock);
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)addr;
spin_lock_bh(&conn->session->lock);
sprintf(buf, "%pI6", &sin6->sin6_addr);
*port = be16_to_cpu(sin6->sin6_port);
spin_unlock_bh(&conn->session->lock);
break;
}
free_addr:
kfree(addr);
return rc;
}
static int
iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
int is_leading)
{
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
struct iscsi_host *ihost = shost_priv(shost);
struct iscsi_session *session = cls_session->dd_data;
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
@ -670,27 +628,15 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,
"sockfd_lookup failed %d\n", err);
return -EEXIST;
}
/*
* copy these values now because if we drop the session
* userspace may still want to query the values since we will
* be using them for the reconnect
*/
err = iscsi_sw_tcp_get_addr(conn, sock, conn->portal_address,
&conn->portal_port, kernel_getpeername);
if (err)
goto free_socket;
err = iscsi_sw_tcp_get_addr(conn, sock, ihost->local_address,
&ihost->local_port, kernel_getsockname);
if (err)
goto free_socket;
err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
if (err)
goto free_socket;
spin_lock_bh(&session->lock);
/* bind iSCSI connection and socket */
tcp_sw_conn->sock = sock;
spin_unlock_bh(&session->lock);
/* setup Socket parameters */
sk = sock->sk;
@ -752,24 +698,74 @@ static int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
{
struct iscsi_conn *conn = cls_conn->dd_data;
int len;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct sockaddr_in6 addr;
int rc, len;
switch(param) {
case ISCSI_PARAM_CONN_PORT:
spin_lock_bh(&conn->session->lock);
len = sprintf(buf, "%hu\n", conn->portal_port);
spin_unlock_bh(&conn->session->lock);
break;
case ISCSI_PARAM_CONN_ADDRESS:
spin_lock_bh(&conn->session->lock);
len = sprintf(buf, "%s\n", conn->portal_address);
if (!tcp_sw_conn || !tcp_sw_conn->sock) {
spin_unlock_bh(&conn->session->lock);
return -ENOTCONN;
}
rc = kernel_getpeername(tcp_sw_conn->sock,
(struct sockaddr *)&addr, &len);
spin_unlock_bh(&conn->session->lock);
break;
if (rc)
return rc;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
&addr, param, buf);
default:
return iscsi_conn_get_param(cls_conn, param, buf);
}
return len;
return 0;
}
static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf)
{
struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(shost);
struct iscsi_session *session = tcp_sw_host->session;
struct iscsi_conn *conn;
struct iscsi_tcp_conn *tcp_conn;
struct iscsi_sw_tcp_conn *tcp_sw_conn;
struct sockaddr_in6 addr;
int rc, len;
switch (param) {
case ISCSI_HOST_PARAM_IPADDRESS:
spin_lock_bh(&session->lock);
conn = session->leadconn;
if (!conn) {
spin_unlock_bh(&session->lock);
return -ENOTCONN;
}
tcp_conn = conn->dd_data;
tcp_sw_conn = tcp_conn->dd_data;
if (!tcp_sw_conn->sock) {
spin_unlock_bh(&session->lock);
return -ENOTCONN;
}
rc = kernel_getsockname(tcp_sw_conn->sock,
(struct sockaddr *)&addr, &len);
spin_unlock_bh(&session->lock);
if (rc)
return rc;
return iscsi_conn_get_addr_param((struct sockaddr_storage *)
&addr, param, buf);
default:
return iscsi_host_get_param(shost, param, buf);
}
return 0;
}
static void
@ -797,6 +793,7 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
struct iscsi_sw_tcp_host *tcp_sw_host;
struct Scsi_Host *shost;
if (ep) {
@ -804,7 +801,8 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
return NULL;
}
shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, 0, 1);
shost = iscsi_host_alloc(&iscsi_sw_tcp_sht,
sizeof(struct iscsi_sw_tcp_host), 1);
if (!shost)
return NULL;
shost->transportt = iscsi_sw_tcp_scsi_transport;
@ -825,6 +823,8 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
if (!cls_session)
goto remove_host;
session = cls_session->dd_data;
tcp_sw_host = iscsi_host_priv(shost);
tcp_sw_host->session = session;
shost->can_queue = session->scsi_cmds_max;
if (iscsi_tcp_r2tpool_alloc(session))
@ -929,7 +929,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_sw_tcp_conn_stop,
/* iscsi host params */
.get_host_param = iscsi_host_get_param,
.get_host_param = iscsi_sw_tcp_host_get_param,
.set_host_param = iscsi_host_set_param,
/* IO */
.send_pdu = iscsi_conn_send_pdu,

View file

@ -55,6 +55,10 @@ struct iscsi_sw_tcp_conn {
ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int);
};
struct iscsi_sw_tcp_host {
struct iscsi_session *session;
};
struct iscsi_sw_tcp_hdrbuf {
struct iscsi_hdr hdrbuf;
char hdrextbuf[ISCSI_MAX_AHS_SIZE +

View file

@ -38,7 +38,7 @@ u16 fc_cpu_mask; /* cpu mask for possible cpus */
EXPORT_SYMBOL(fc_cpu_mask);
static u16 fc_cpu_order; /* 2's power to represent total possible cpus */
static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
struct workqueue_struct *fc_exch_workqueue;
static struct workqueue_struct *fc_exch_workqueue;
/*
* Structure and function definitions for managing Fibre Channel Exchanges
@ -558,6 +558,22 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
return sp;
}
/*
* Set the response handler for the exchange associated with a sequence.
*/
static void fc_seq_set_resp(struct fc_seq *sp,
void (*resp)(struct fc_seq *, struct fc_frame *,
void *),
void *arg)
{
struct fc_exch *ep = fc_seq_exch(sp);
spin_lock_bh(&ep->ex_lock);
ep->resp = resp;
ep->arg = arg;
spin_unlock_bh(&ep->ex_lock);
}
/**
* fc_seq_exch_abort() - Abort an exchange and sequence
* @req_sp: The sequence to be aborted
@ -650,13 +666,10 @@ static void fc_exch_timeout(struct work_struct *work)
if (e_stat & ESB_ST_ABNORMAL)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
fc_exch_delete(ep);
if (resp)
resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
if (!rc) {
/* delete the exchange if it's already being aborted */
fc_exch_delete(ep);
return;
}
fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
goto done;
}
@ -1266,6 +1279,8 @@ free:
* @fp: The request frame
*
* On success, the sequence pointer will be returned and also in fr_seq(@fp).
* A reference will be held on the exchange/sequence for the caller, which
* must call fc_seq_release().
*/
static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
{
@ -1282,6 +1297,15 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
return fr_seq(fp);
}
/**
* fc_seq_release() - Release the hold
* @sp: The sequence.
*/
static void fc_seq_release(struct fc_seq *sp)
{
fc_exch_release(fc_seq_exch(sp));
}
/**
* fc_exch_recv_req() - Handler for an incoming request
* @lport: The local port that received the request
@ -2151,6 +2175,7 @@ err:
fc_exch_mgr_del(ema);
return -ENOMEM;
}
EXPORT_SYMBOL(fc_exch_mgr_list_clone);
/**
* fc_exch_mgr_alloc() - Allocate an exchange manager
@ -2253,17 +2278,46 @@ void fc_exch_mgr_free(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_exch_mgr_free);
/**
* fc_find_ema() - Lookup and return appropriate Exchange Manager Anchor depending
* upon 'xid'.
* @f_ctl: f_ctl
* @lport: The local port the frame was received on
* @fh: The received frame header
*/
static struct fc_exch_mgr_anchor *fc_find_ema(u32 f_ctl,
struct fc_lport *lport,
struct fc_frame_header *fh)
{
struct fc_exch_mgr_anchor *ema;
u16 xid;
if (f_ctl & FC_FC_EX_CTX)
xid = ntohs(fh->fh_ox_id);
else {
xid = ntohs(fh->fh_rx_id);
if (xid == FC_XID_UNKNOWN)
return list_entry(lport->ema_list.prev,
typeof(*ema), ema_list);
}
list_for_each_entry(ema, &lport->ema_list, ema_list) {
if ((xid >= ema->mp->min_xid) &&
(xid <= ema->mp->max_xid))
return ema;
}
return NULL;
}
/**
* fc_exch_recv() - Handler for received frames
* @lport: The local port the frame was received on
* @fp: The received frame
* @fp: The received frame
*/
void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
struct fc_exch_mgr_anchor *ema;
u32 f_ctl, found = 0;
u16 oxid;
u32 f_ctl;
/* lport lock ? */
if (!lport || lport->state == LPORT_ST_DISABLED) {
@ -2274,24 +2328,17 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
}
f_ctl = ntoh24(fh->fh_f_ctl);
oxid = ntohs(fh->fh_ox_id);
if (f_ctl & FC_FC_EX_CTX) {
list_for_each_entry(ema, &lport->ema_list, ema_list) {
if ((oxid >= ema->mp->min_xid) &&
(oxid <= ema->mp->max_xid)) {
found = 1;
break;
}
}
if (!found) {
FC_LPORT_DBG(lport, "Received response for out "
"of range oxid:%hx\n", oxid);
fc_frame_free(fp);
return;
}
} else
ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list);
ema = fc_find_ema(f_ctl, lport, fh);
if (!ema) {
FC_LPORT_DBG(lport, "Unable to find Exchange Manager Anchor,"
"fc_ctl <0x%x>, xid <0x%x>\n",
f_ctl,
(f_ctl & FC_FC_EX_CTX) ?
ntohs(fh->fh_ox_id) :
ntohs(fh->fh_rx_id));
fc_frame_free(fp);
return;
}
/*
* If frame is marked invalid, just drop it.
@ -2329,6 +2376,9 @@ int fc_exch_init(struct fc_lport *lport)
if (!lport->tt.seq_start_next)
lport->tt.seq_start_next = fc_seq_start_next;
if (!lport->tt.seq_set_resp)
lport->tt.seq_set_resp = fc_seq_set_resp;
if (!lport->tt.exch_seq_send)
lport->tt.exch_seq_send = fc_exch_seq_send;
@ -2350,6 +2400,9 @@ int fc_exch_init(struct fc_lport *lport)
if (!lport->tt.seq_assign)
lport->tt.seq_assign = fc_seq_assign;
if (!lport->tt.seq_release)
lport->tt.seq_release = fc_seq_release;
return 0;
}
EXPORT_SYMBOL(fc_exch_init);
@ -2357,7 +2410,7 @@ EXPORT_SYMBOL(fc_exch_init);
/**
* fc_setup_exch_mgr() - Setup an exchange manager
*/
int fc_setup_exch_mgr()
int fc_setup_exch_mgr(void)
{
fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
0, SLAB_HWCACHE_ALIGN, NULL);
@ -2395,7 +2448,7 @@ int fc_setup_exch_mgr()
/**
* fc_destroy_exch_mgr() - Destroy an exchange manager
*/
void fc_destroy_exch_mgr()
void fc_destroy_exch_mgr(void)
{
destroy_workqueue(fc_exch_workqueue);
kmem_cache_destroy(fc_em_cachep);

View file

@ -42,7 +42,7 @@
#include "fc_libfc.h"
struct kmem_cache *scsi_pkt_cachep;
static struct kmem_cache *scsi_pkt_cachep;
/* SRB state definitions */
#define FC_SRB_FREE 0 /* cmd is free */
@ -155,6 +155,7 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp)
if (fsp) {
memset(fsp, 0, sizeof(*fsp));
fsp->lp = lport;
fsp->xfer_ddp = FC_XID_UNKNOWN;
atomic_set(&fsp->ref_cnt, 1);
init_timer(&fsp->timer);
INIT_LIST_HEAD(&fsp->list);
@ -1201,6 +1202,7 @@ unlock:
static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
{
int rc = FAILED;
unsigned long ticks_left;
if (fc_fcp_send_abort(fsp))
return FAILED;
@ -1209,13 +1211,13 @@ static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
fsp->wait_for_comp = 1;
spin_unlock_bh(&fsp->scsi_pkt_lock);
rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV);
ticks_left = wait_for_completion_timeout(&fsp->tm_done,
FC_SCSI_TM_TOV);
spin_lock_bh(&fsp->scsi_pkt_lock);
fsp->wait_for_comp = 0;
if (!rc) {
if (!ticks_left) {
FC_FCP_DBG(fsp, "target abort cmd failed\n");
rc = FAILED;
} else if (fsp->state & FC_SRB_ABORTED) {
FC_FCP_DBG(fsp, "target abort cmd passed\n");
rc = SUCCESS;
@ -1321,7 +1323,7 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
*
* scsi-eh will escalate for when either happens.
*/
goto out;
return;
}
if (fc_fcp_lock_pkt(fsp))
@ -1787,15 +1789,14 @@ static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
/**
* fc_queuecommand() - The queuecommand function of the SCSI template
* @shost: The Scsi_Host that the command was issued to
* @cmd: The scsi_cmnd to be executed
* @done: The callback function to be called when the scsi_cmnd is complete
*
* This is the i/o strategy routine, called by the SCSI layer. This routine
* is called with the host_lock held.
* This is the i/o strategy routine, called by the SCSI layer.
*/
static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)
{
struct fc_lport *lport;
struct fc_lport *lport = shost_priv(shost);
struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
struct fc_fcp_pkt *fsp;
struct fc_rport_libfc_priv *rpriv;
@ -1803,15 +1804,12 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
int rc = 0;
struct fcoe_dev_stats *stats;
lport = shost_priv(sc_cmd->device->host);
rval = fc_remote_port_chkready(rport);
if (rval) {
sc_cmd->result = rval;
done(sc_cmd);
sc_cmd->scsi_done(sc_cmd);
return 0;
}
spin_unlock_irq(lport->host->host_lock);
if (!*(struct fc_remote_port **)rport->dd_data) {
/*
@ -1819,7 +1817,7 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
* online
*/
sc_cmd->result = DID_IMM_RETRY << 16;
done(sc_cmd);
sc_cmd->scsi_done(sc_cmd);
goto out;
}
@ -1842,10 +1840,7 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
* build the libfc request pkt
*/
fsp->cmd = sc_cmd; /* save the cmd */
fsp->lp = lport; /* save the softc ptr */
fsp->rport = rport; /* set the remote port ptr */
fsp->xfer_ddp = FC_XID_UNKNOWN;
sc_cmd->scsi_done = done;
/*
* set up the transfer length
@ -1886,11 +1881,8 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
rc = SCSI_MLQUEUE_HOST_BUSY;
}
out:
spin_lock_irq(lport->host->host_lock);
return rc;
}
DEF_SCSI_QCMD(fc_queuecommand)
EXPORT_SYMBOL(fc_queuecommand);
/**
@ -2112,7 +2104,6 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
* the sc passed in is not setup for execution like when sent
* through the queuecommand callout.
*/
fsp->lp = lport; /* save the softc ptr */
fsp->rport = rport; /* set the remote port ptr */
/*
@ -2245,7 +2236,7 @@ void fc_fcp_destroy(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_fcp_destroy);
int fc_setup_fcp()
int fc_setup_fcp(void)
{
int rc = 0;
@ -2261,7 +2252,7 @@ int fc_setup_fcp()
return rc;
}
void fc_destroy_fcp()
void fc_destroy_fcp(void)
{
if (scsi_pkt_cachep)
kmem_cache_destroy(scsi_pkt_cachep);

View file

@ -35,6 +35,27 @@ unsigned int fc_debug_logging;
module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
DEFINE_MUTEX(fc_prov_mutex);
static LIST_HEAD(fc_local_ports);
struct blocking_notifier_head fc_lport_notifier_head =
BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head);
EXPORT_SYMBOL(fc_lport_notifier_head);
/*
* Providers which primarily send requests and PRLIs.
*/
struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = {
[0] = &fc_rport_t0_prov,
[FC_TYPE_FCP] = &fc_rport_fcp_init,
};
/*
* Providers which receive requests.
*/
struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = {
[FC_TYPE_ELS] = &fc_lport_els_prov,
};
/**
* libfc_init() - Initialize libfc.ko
*/
@ -210,3 +231,102 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset);
}
EXPORT_SYMBOL(fc_fill_reply_hdr);
/**
* fc_fc4_conf_lport_params() - Modify "service_params" of specified lport
* if there is service provider (target provider) registered with libfc
* for specified "fc_ft_type"
* @lport: Local port which service_params needs to be modified
* @type: FC-4 type, such as FC_TYPE_FCP
*/
void fc_fc4_conf_lport_params(struct fc_lport *lport, enum fc_fh_type type)
{
struct fc4_prov *prov_entry;
BUG_ON(type >= FC_FC4_PROV_SIZE);
BUG_ON(!lport);
prov_entry = fc_passive_prov[type];
if (type == FC_TYPE_FCP) {
if (prov_entry && prov_entry->recv)
lport->service_params |= FCP_SPPF_TARG_FCN;
}
}
void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg)
{
struct fc_lport *lport;
mutex_lock(&fc_prov_mutex);
list_for_each_entry(lport, &fc_local_ports, lport_list)
notify(lport, arg);
mutex_unlock(&fc_prov_mutex);
}
EXPORT_SYMBOL(fc_lport_iterate);
/**
* fc_fc4_register_provider() - register FC-4 upper-level provider.
* @type: FC-4 type, such as FC_TYPE_FCP
* @prov: structure describing provider including ops vector.
*
* Returns 0 on success, negative error otherwise.
*/
int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov)
{
struct fc4_prov **prov_entry;
int ret = 0;
if (type >= FC_FC4_PROV_SIZE)
return -EINVAL;
mutex_lock(&fc_prov_mutex);
prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type;
if (*prov_entry)
ret = -EBUSY;
else
*prov_entry = prov;
mutex_unlock(&fc_prov_mutex);
return ret;
}
EXPORT_SYMBOL(fc_fc4_register_provider);
/**
* fc_fc4_deregister_provider() - deregister FC-4 upper-level provider.
* @type: FC-4 type, such as FC_TYPE_FCP
* @prov: structure describing provider including ops vector.
*/
void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
{
BUG_ON(type >= FC_FC4_PROV_SIZE);
mutex_lock(&fc_prov_mutex);
if (prov->recv)
rcu_assign_pointer(fc_passive_prov[type], NULL);
else
rcu_assign_pointer(fc_active_prov[type], NULL);
mutex_unlock(&fc_prov_mutex);
synchronize_rcu();
}
EXPORT_SYMBOL(fc_fc4_deregister_provider);
/**
* fc_fc4_add_lport() - add new local port to list and run notifiers.
* @lport: The new local port.
*/
void fc_fc4_add_lport(struct fc_lport *lport)
{
mutex_lock(&fc_prov_mutex);
list_add_tail(&lport->lport_list, &fc_local_ports);
blocking_notifier_call_chain(&fc_lport_notifier_head,
FC_LPORT_EV_ADD, lport);
mutex_unlock(&fc_prov_mutex);
}
/**
* fc_fc4_del_lport() - remove local port from list and run notifiers.
* @lport: The new local port.
*/
void fc_fc4_del_lport(struct fc_lport *lport)
{
mutex_lock(&fc_prov_mutex);
list_del(&lport->lport_list);
blocking_notifier_call_chain(&fc_lport_notifier_head,
FC_LPORT_EV_DEL, lport);
mutex_unlock(&fc_prov_mutex);
}

View file

@ -93,6 +93,17 @@ extern unsigned int fc_debug_logging;
printk(KERN_INFO "host%u: scsi: " fmt, \
(lport)->host->host_no, ##args))
/*
* FC-4 Providers.
*/
extern struct fc4_prov *fc_active_prov[]; /* providers without recv */
extern struct fc4_prov *fc_passive_prov[]; /* providers with recv */
extern struct mutex fc_prov_mutex; /* lock over table changes */
extern struct fc4_prov fc_rport_t0_prov; /* type 0 provider */
extern struct fc4_prov fc_lport_els_prov; /* ELS provider */
extern struct fc4_prov fc_rport_fcp_init; /* FCP initiator provider */
/*
* Set up direct-data placement for this I/O request
*/
@ -112,6 +123,9 @@ void fc_destroy_fcp(void);
* Internal libfc functions
*/
const char *fc_els_resp_type(struct fc_frame *);
extern void fc_fc4_add_lport(struct fc_lport *);
extern void fc_fc4_del_lport(struct fc_lport *);
extern void fc_fc4_conf_lport_params(struct fc_lport *, enum fc_fh_type);
/*
* Copies a buffer into an sg list

View file

@ -633,6 +633,7 @@ int fc_lport_destroy(struct fc_lport *lport)
lport->tt.fcp_abort_io(lport);
lport->tt.disc_stop_final(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
fc_fc4_del_lport(lport);
return 0;
}
EXPORT_SYMBOL(fc_lport_destroy);
@ -849,7 +850,7 @@ out:
}
/**
* fc_lport_recv_req() - The generic lport request handler
* fc_lport_recv_els_req() - The generic lport ELS request handler
* @lport: The local port that received the request
* @fp: The request frame
*
@ -859,9 +860,9 @@ out:
* Locking Note: This function should not be called with the lport
* lock held becuase it will grab the lock.
*/
static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
static void fc_lport_recv_els_req(struct fc_lport *lport,
struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
void (*recv)(struct fc_lport *, struct fc_frame *);
mutex_lock(&lport->lp_mutex);
@ -873,8 +874,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
*/
if (!lport->link_up)
fc_frame_free(fp);
else if (fh->fh_type == FC_TYPE_ELS &&
fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
else {
/*
* Check opcode.
*/
@ -903,14 +903,62 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
}
recv(lport, fp);
} else {
FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
fr_eof(fp));
fc_frame_free(fp);
}
mutex_unlock(&lport->lp_mutex);
}
static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len,
const struct fc_els_spp *spp_in,
struct fc_els_spp *spp_out)
{
return FC_SPP_RESP_INVL;
}
struct fc4_prov fc_lport_els_prov = {
.prli = fc_lport_els_prli,
.recv = fc_lport_recv_els_req,
};
/**
* fc_lport_recv_req() - The generic lport request handler
* @lport: The lport that received the request
* @fp: The frame the request is in
*
* Locking Note: This function should not be called with the lport
* lock held becuase it may grab the lock.
*/
static void fc_lport_recv_req(struct fc_lport *lport,
struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
struct fc_seq *sp = fr_seq(fp);
struct fc4_prov *prov;
/*
* Use RCU read lock and module_lock to be sure module doesn't
* deregister and get unloaded while we're calling it.
* try_module_get() is inlined and accepts a NULL parameter.
* Only ELSes and FCP target ops should come through here.
* The locking is unfortunate, and a better scheme is being sought.
*/
rcu_read_lock();
if (fh->fh_type >= FC_FC4_PROV_SIZE)
goto drop;
prov = rcu_dereference(fc_passive_prov[fh->fh_type]);
if (!prov || !try_module_get(prov->module))
goto drop;
rcu_read_unlock();
prov->recv(lport, fp);
module_put(prov->module);
return;
drop:
rcu_read_unlock();
FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
fc_frame_free(fp);
lport->tt.exch_done(sp);
}
/**
* fc_lport_reset() - Reset a local port
* @lport: The local port which should be reset
@ -1542,6 +1590,7 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
*/
int fc_lport_config(struct fc_lport *lport)
{
INIT_LIST_HEAD(&lport->ema_list);
INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
mutex_init(&lport->lp_mutex);
@ -1549,6 +1598,7 @@ int fc_lport_config(struct fc_lport *lport)
fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
fc_lport_add_fc4_type(lport, FC_TYPE_CT);
fc_fc4_conf_lport_params(lport, FC_TYPE_FCP);
return 0;
}
@ -1586,6 +1636,7 @@ int fc_lport_init(struct fc_lport *lport)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
fc_fc4_add_lport(lport);
return 0;
}

View file

@ -37,9 +37,7 @@ struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)
vn_port = libfc_host_alloc(shost->hostt, privsize);
if (!vn_port)
goto err_out;
if (fc_exch_mgr_list_clone(n_port, vn_port))
goto err_put;
return vn_port;
vn_port->vport = vport;
vport->dd_data = vn_port;
@ -49,11 +47,6 @@ struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)
mutex_unlock(&n_port->lp_mutex);
return vn_port;
err_put:
scsi_host_put(vn_port->host);
err_out:
return NULL;
}
EXPORT_SYMBOL(libfc_vport_create);
@ -86,6 +79,7 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
return lport;
}
EXPORT_SYMBOL(fc_vport_id_lookup);
/*
* When setting the link state of vports during an lport state change, it's

View file

@ -58,7 +58,7 @@
#include "fc_libfc.h"
struct workqueue_struct *rport_event_queue;
static struct workqueue_struct *rport_event_queue;
static void fc_rport_enter_flogi(struct fc_rport_priv *);
static void fc_rport_enter_plogi(struct fc_rport_priv *);
@ -145,8 +145,10 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
INIT_WORK(&rdata->event_work, fc_rport_work);
if (port_id != FC_FID_DIR_SERV)
if (port_id != FC_FID_DIR_SERV) {
rdata->lld_event_callback = lport->tt.rport_event_callback;
list_add_rcu(&rdata->peers, &lport->disc.rports);
}
return rdata;
}
@ -257,6 +259,8 @@ static void fc_rport_work(struct work_struct *work)
struct fc_rport_operations *rport_ops;
struct fc_rport_identifiers ids;
struct fc_rport *rport;
struct fc4_prov *prov;
u8 type;
mutex_lock(&rdata->rp_mutex);
event = rdata->event;
@ -300,12 +304,25 @@ static void fc_rport_work(struct work_struct *work)
FC_RPORT_DBG(rdata, "callback ev %d\n", event);
rport_ops->event_callback(lport, rdata, event);
}
if (rdata->lld_event_callback) {
FC_RPORT_DBG(rdata, "lld callback ev %d\n", event);
rdata->lld_event_callback(lport, rdata, event);
}
kref_put(&rdata->kref, lport->tt.rport_destroy);
break;
case RPORT_EV_FAILED:
case RPORT_EV_LOGO:
case RPORT_EV_STOP:
if (rdata->prli_count) {
mutex_lock(&fc_prov_mutex);
for (type = 1; type < FC_FC4_PROV_SIZE; type++) {
prov = fc_passive_prov[type];
if (prov && prov->prlo)
prov->prlo(rdata);
}
mutex_unlock(&fc_prov_mutex);
}
port_id = rdata->ids.port_id;
mutex_unlock(&rdata->rp_mutex);
@ -313,6 +330,10 @@ static void fc_rport_work(struct work_struct *work)
FC_RPORT_DBG(rdata, "callback ev %d\n", event);
rport_ops->event_callback(lport, rdata, event);
}
if (rdata->lld_event_callback) {
FC_RPORT_DBG(rdata, "lld callback ev %d\n", event);
rdata->lld_event_callback(lport, rdata, event);
}
cancel_delayed_work_sync(&rdata->retry_work);
/*
@ -336,6 +357,7 @@ static void fc_rport_work(struct work_struct *work)
if (port_id == FC_FID_DIR_SERV) {
rdata->event = RPORT_EV_NONE;
mutex_unlock(&rdata->rp_mutex);
kref_put(&rdata->kref, lport->tt.rport_destroy);
} else if ((rdata->flags & FC_RP_STARTED) &&
rdata->major_retries <
lport->max_rport_retry_count) {
@ -575,7 +597,7 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
/* make sure this isn't an FC_EX_CLOSED error, never retry those */
if (PTR_ERR(fp) == -FC_EX_CLOSED)
return fc_rport_error(rdata, fp);
goto out;
if (rdata->retries < rdata->local_port->max_rport_retry_count) {
FC_RPORT_DBG(rdata, "Error %ld in state %s, retrying\n",
@ -588,7 +610,8 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
return;
}
return fc_rport_error(rdata, fp);
out:
fc_rport_error(rdata, fp);
}
/**
@ -878,6 +901,9 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);
rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn);
/* save plogi response sp_features for further reference */
rdata->sp_features = ntohs(plp->fl_csp.sp_features);
if (lport->point_to_multipoint)
fc_rport_login_complete(rdata, fp);
csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
@ -949,6 +975,8 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_els_prli prli;
struct fc_els_spp spp;
} *pp;
struct fc_els_spp temp_spp;
struct fc4_prov *prov;
u32 roles = FC_RPORT_ROLE_UNKNOWN;
u32 fcp_parm = 0;
u8 op;
@ -983,6 +1011,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
resp_code = (pp->spp.spp_flags & FC_SPP_RESP_MASK);
FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x\n",
pp->spp.spp_flags);
rdata->spp_type = pp->spp.spp_type;
if (resp_code != FC_SPP_RESP_ACK) {
if (resp_code == FC_SPP_RESP_CONF)
fc_rport_error(rdata, fp);
@ -996,6 +1025,15 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
fcp_parm = ntohl(pp->spp.spp_params);
if (fcp_parm & FCP_SPPF_RETRY)
rdata->flags |= FC_RP_FLAGS_RETRY;
if (fcp_parm & FCP_SPPF_CONF_COMPL)
rdata->flags |= FC_RP_FLAGS_CONF_REQ;
prov = fc_passive_prov[FC_TYPE_FCP];
if (prov) {
memset(&temp_spp, 0, sizeof(temp_spp));
prov->prli(rdata, pp->prli.prli_spp_len,
&pp->spp, &temp_spp);
}
rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
@ -1033,6 +1071,7 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
struct fc_els_spp spp;
} *pp;
struct fc_frame *fp;
struct fc4_prov *prov;
/*
* If the rport is one of the well known addresses
@ -1054,9 +1093,20 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
return;
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI,
fc_rport_prli_resp, rdata,
2 * lport->r_a_tov))
fc_prli_fill(lport, fp);
prov = fc_passive_prov[FC_TYPE_FCP];
if (prov) {
pp = fc_frame_payload_get(fp, sizeof(*pp));
prov->prli(rdata, sizeof(pp->spp), NULL, &pp->spp);
}
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rdata->ids.port_id,
fc_host_port_id(lport->host), FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
if (!lport->tt.exch_seq_send(lport, fp, fc_rport_prli_resp,
NULL, rdata, 2 * lport->r_a_tov))
fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
@ -1642,9 +1692,9 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
unsigned int len;
unsigned int plen;
enum fc_els_spp_resp resp;
enum fc_els_spp_resp passive;
struct fc_seq_els_data rjt_data;
u32 fcp_parm;
u32 roles = FC_RPORT_ROLE_UNKNOWN;
struct fc4_prov *prov;
FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
fc_rport_state(rdata));
@ -1678,46 +1728,42 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
pp->prli.prli_len = htons(len);
len -= sizeof(struct fc_els_prli);
/* reinitialize remote port roles */
rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
/*
* Go through all the service parameter pages and build
* response. If plen indicates longer SPP than standard,
* use that. The entire response has been pre-cleared above.
*/
spp = &pp->spp;
mutex_lock(&fc_prov_mutex);
while (len >= plen) {
rdata->spp_type = rspp->spp_type;
spp->spp_type = rspp->spp_type;
spp->spp_type_ext = rspp->spp_type_ext;
spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
resp = FC_SPP_RESP_ACK;
resp = 0;
switch (rspp->spp_type) {
case 0: /* common to all FC-4 types */
break;
case FC_TYPE_FCP:
fcp_parm = ntohl(rspp->spp_params);
if (fcp_parm & FCP_SPPF_RETRY)
rdata->flags |= FC_RP_FLAGS_RETRY;
rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcp_parm & FCP_SPPF_TARG_FCN)
roles |= FC_RPORT_ROLE_FCP_TARGET;
rdata->ids.roles = roles;
spp->spp_params = htonl(lport->service_params);
break;
default:
resp = FC_SPP_RESP_INVL;
break;
if (rspp->spp_type < FC_FC4_PROV_SIZE) {
prov = fc_active_prov[rspp->spp_type];
if (prov)
resp = prov->prli(rdata, plen, rspp, spp);
prov = fc_passive_prov[rspp->spp_type];
if (prov) {
passive = prov->prli(rdata, plen, rspp, spp);
if (!resp || passive == FC_SPP_RESP_ACK)
resp = passive;
}
}
if (!resp) {
if (spp->spp_flags & FC_SPP_EST_IMG_PAIR)
resp |= FC_SPP_RESP_CONF;
else
resp |= FC_SPP_RESP_INVL;
}
spp->spp_flags |= resp;
len -= plen;
rspp = (struct fc_els_spp *)((char *)rspp + plen);
spp = (struct fc_els_spp *)((char *)spp + plen);
}
mutex_unlock(&fc_prov_mutex);
/*
* Send LS_ACC. If this fails, the originator should retry.
@ -1886,10 +1932,83 @@ int fc_rport_init(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_rport_init);
/**
* fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator.
* @rdata: remote port private
* @spp_len: service parameter page length
* @rspp: received service parameter page
* @spp: response service parameter page
*
* Returns the value for the response code to be placed in spp_flags;
* Returns 0 if not an initiator.
*/
static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len,
const struct fc_els_spp *rspp,
struct fc_els_spp *spp)
{
struct fc_lport *lport = rdata->local_port;
u32 fcp_parm;
fcp_parm = ntohl(rspp->spp_params);
rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
if (fcp_parm & FCP_SPPF_INIT_FCN)
rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcp_parm & FCP_SPPF_TARG_FCN)
rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
if (fcp_parm & FCP_SPPF_RETRY)
rdata->flags |= FC_RP_FLAGS_RETRY;
rdata->supported_classes = FC_COS_CLASS3;
if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR))
return 0;
spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
/*
* OR in our service parameters with other providers (target), if any.
*/
fcp_parm = ntohl(spp->spp_params);
spp->spp_params = htonl(fcp_parm | lport->service_params);
return FC_SPP_RESP_ACK;
}
/*
* FC-4 provider ops for FCP initiator.
*/
struct fc4_prov fc_rport_fcp_init = {
.prli = fc_rport_fcp_prli,
};
/**
* fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0
* @rdata: remote port private
* @spp_len: service parameter page length
* @rspp: received service parameter page
* @spp: response service parameter page
*/
static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len,
const struct fc_els_spp *rspp,
struct fc_els_spp *spp)
{
if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR)
return FC_SPP_RESP_INVL;
return FC_SPP_RESP_ACK;
}
/*
* FC-4 provider ops for type 0 service parameters.
*
* This handles the special case of type 0 which is always successful
* but doesn't do anything otherwise.
*/
struct fc4_prov fc_rport_t0_prov = {
.prli = fc_rport_t0_prli,
};
/**
* fc_setup_rport() - Initialize the rport_event_queue
*/
int fc_setup_rport()
int fc_setup_rport(void)
{
rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
if (!rport_event_queue)
@ -1900,7 +2019,7 @@ int fc_setup_rport()
/**
* fc_destroy_rport() - Destroy the rport_event_queue
*/
void fc_destroy_rport()
void fc_destroy_rport(void)
{
destroy_workqueue(rport_event_queue);
}

View file

@ -3352,6 +3352,47 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
}
EXPORT_SYMBOL_GPL(iscsi_session_get_param);
int iscsi_conn_get_addr_param(struct sockaddr_storage *addr,
enum iscsi_param param, char *buf)
{
struct sockaddr_in6 *sin6 = NULL;
struct sockaddr_in *sin = NULL;
int len;
switch (addr->ss_family) {
case AF_INET:
sin = (struct sockaddr_in *)addr;
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)addr;
break;
default:
return -EINVAL;
}
switch (param) {
case ISCSI_PARAM_CONN_ADDRESS:
case ISCSI_HOST_PARAM_IPADDRESS:
if (sin)
len = sprintf(buf, "%pI4\n", &sin->sin_addr.s_addr);
else
len = sprintf(buf, "%pI6\n", &sin6->sin6_addr);
break;
case ISCSI_PARAM_CONN_PORT:
if (sin)
len = sprintf(buf, "%hu\n", be16_to_cpu(sin->sin_port));
else
len = sprintf(buf, "%hu\n",
be16_to_cpu(sin6->sin6_port));
break;
default:
return -EINVAL;
}
return len;
}
EXPORT_SYMBOL_GPL(iscsi_conn_get_addr_param);
int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
{
@ -3416,9 +3457,6 @@ int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
case ISCSI_HOST_PARAM_INITIATOR_NAME:
len = sprintf(buf, "%s\n", ihost->initiatorname);
break;
case ISCSI_HOST_PARAM_IPADDRESS:
len = sprintf(buf, "%s\n", ihost->local_address);
break;
default:
return -ENOSYS;
}

View file

@ -46,11 +46,3 @@ config SCSI_SAS_HOST_SMP
Allows sas hosts to receive SMP frames. Selecting this
option builds an SMP interpreter into libsas. Say
N here if you want to save the few kb this consumes.
config SCSI_SAS_LIBSAS_DEBUG
bool "Compile the SAS Domain Transport Attributes in debug mode"
default y
depends on SCSI_SAS_LIBSAS
help
Compiles the SAS Layer in debug mode. In debug mode, the
SAS Layer prints diagnostic and debug messages.

View file

@ -21,10 +21,6 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
ifeq ($(CONFIG_SCSI_SAS_LIBSAS_DEBUG),y)
EXTRA_CFLAGS += -DSAS_DEBUG
endif
obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas.o
libsas-y += sas_init.o \
sas_phy.o \

View file

@ -71,13 +71,13 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts)
case SAS_SG_ERR:
return AC_ERR_INVALID;
case SAM_STAT_CHECK_CONDITION:
case SAS_OPEN_TO:
case SAS_OPEN_REJECT:
SAS_DPRINTK("%s: Saw error %d. What to do?\n",
__func__, ts->stat);
return AC_ERR_OTHER;
case SAM_STAT_CHECK_CONDITION:
case SAS_ABORTED_TASK:
return AC_ERR_DEV;
@ -107,13 +107,15 @@ static void sas_ata_task_done(struct sas_task *task)
sas_ha = dev->port->ha;
spin_lock_irqsave(dev->sata_dev.ap->lock, flags);
if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD) {
if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD ||
((stat->stat == SAM_STAT_CHECK_CONDITION &&
dev->sata_dev.command_set == ATAPI_COMMAND_SET))) {
ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf);
qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command);
dev->sata_dev.sstatus = resp->sstatus;
dev->sata_dev.serror = resp->serror;
dev->sata_dev.scontrol = resp->scontrol;
} else if (stat->stat != SAM_STAT_GOOD) {
} else {
ac = sas_to_ata_err(stat);
if (ac) {
SAS_DPRINTK("%s: SAS error %x\n", __func__,
@ -305,55 +307,6 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc)
}
}
static int sas_ata_scr_write(struct ata_link *link, unsigned int sc_reg_in,
u32 val)
{
struct domain_device *dev = link->ap->private_data;
SAS_DPRINTK("STUB %s\n", __func__);
switch (sc_reg_in) {
case SCR_STATUS:
dev->sata_dev.sstatus = val;
break;
case SCR_CONTROL:
dev->sata_dev.scontrol = val;
break;
case SCR_ERROR:
dev->sata_dev.serror = val;
break;
case SCR_ACTIVE:
dev->sata_dev.ap->link.sactive = val;
break;
default:
return -EINVAL;
}
return 0;
}
static int sas_ata_scr_read(struct ata_link *link, unsigned int sc_reg_in,
u32 *val)
{
struct domain_device *dev = link->ap->private_data;
SAS_DPRINTK("STUB %s\n", __func__);
switch (sc_reg_in) {
case SCR_STATUS:
*val = dev->sata_dev.sstatus;
return 0;
case SCR_CONTROL:
*val = dev->sata_dev.scontrol;
return 0;
case SCR_ERROR:
*val = dev->sata_dev.serror;
return 0;
case SCR_ACTIVE:
*val = dev->sata_dev.ap->link.sactive;
return 0;
default:
return -EINVAL;
}
}
static struct ata_port_operations sas_sata_ops = {
.prereset = ata_std_prereset,
.softreset = NULL,
@ -367,8 +320,6 @@ static struct ata_port_operations sas_sata_ops = {
.qc_fill_rtf = sas_ata_qc_fill_rtf,
.port_start = ata_sas_port_start,
.port_stop = ata_sas_port_stop,
.scr_read = sas_ata_scr_read,
.scr_write = sas_ata_scr_write
};
static struct ata_port_info sata_port_info = {
@ -801,7 +752,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)
if (!dev_is_sata(ddev))
continue;
ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler");
ata_scsi_port_error_handler(shost, ap);
}
@ -834,13 +785,13 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
LIST_HEAD(sata_q);
ap = NULL;
list_for_each_entry_safe(cmd, n, work_q, eh_entry) {
struct domain_device *ddev = cmd_to_domain_dev(cmd);
if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd))
continue;
if(ap && ap != ddev->sata_dev.ap)
if (ap && ap != ddev->sata_dev.ap)
continue;
ap = ddev->sata_dev.ap;
rtn = 1;
@ -848,8 +799,21 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
}
if (!list_empty(&sata_q)) {
ata_port_printk(ap, KERN_DEBUG,"sas eh calling libata cmd error handler\n");
ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata cmd error handler\n");
ata_scsi_cmd_error_handler(shost, ap, &sata_q);
/*
* ata's error handler may leave the cmd on the list
* so make sure they don't remain on a stack list
* about to go out of scope.
*
* This looks strange, since the commands are
* now part of no list, but the next error
* action will be ata_port_error_handler()
* which takes no list and sweeps them up
* anyway from the ata tag array.
*/
while (!list_empty(&sata_q))
list_del_init(sata_q.next);
}
} while (ap);

View file

@ -24,8 +24,6 @@
#include "sas_dump.h"
#ifdef SAS_DEBUG
static const char *sas_hae_str[] = {
[0] = "HAE_RESET",
};
@ -72,5 +70,3 @@ void sas_dump_port(struct asd_sas_port *port)
SAS_DPRINTK("port%d: oob_mode:0x%x\n", port->id, port->oob_mode);
SAS_DPRINTK("port%d: num_phys:%d\n", port->id, port->num_phys);
}
#endif /* SAS_DEBUG */

View file

@ -24,19 +24,7 @@
#include "sas_internal.h"
#ifdef SAS_DEBUG
void sas_dprint_porte(int phyid, enum port_event pe);
void sas_dprint_phye(int phyid, enum phy_event pe);
void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he);
void sas_dump_port(struct asd_sas_port *port);
#else /* SAS_DEBUG */
static inline void sas_dprint_porte(int phyid, enum port_event pe) { }
static inline void sas_dprint_phye(int phyid, enum phy_event pe) { }
static inline void sas_dprint_hae(struct sas_ha_struct *sas_ha,
enum ha_event he) { }
static inline void sas_dump_port(struct asd_sas_port *port) { }
#endif /* SAS_DEBUG */

View file

@ -244,6 +244,11 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
* dev to host FIS as described in section G.5 of
* sas-2 r 04b */
dr = &((struct smp_resp *)disc_resp)->disc;
if (memcmp(dev->sas_addr, dr->attached_sas_addr,
SAS_ADDR_SIZE) == 0) {
sas_printk("Found loopback topology, just ignore it!\n");
return 0;
}
if (!(dr->attached_dev_type == 0 &&
dr->attached_sata_dev))
break;

View file

@ -33,11 +33,7 @@
#define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__)
#ifdef SAS_DEBUG
#define SAS_DPRINTK(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__)
#else
#define SAS_DPRINTK(fmt, ...)
#endif
#define SAS_DPRINTK(fmt, ...) printk(KERN_DEBUG "sas: " fmt, ## __VA_ARGS__)
#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble)
#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0)

View file

@ -681,11 +681,10 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
{
struct sas_task *task = TO_SAS_TASK(cmd);
unsigned long flags;
enum blk_eh_timer_return rtn;
enum blk_eh_timer_return rtn;
if (sas_ata_timed_out(cmd, task, &rtn))
return rtn;
if (!task) {
cmd->request->timeout /= 2;

View file

@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
* Copyright (C) 2004-2010 Emulex. All rights reserved. *
* Copyright (C) 2004-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@ -325,6 +325,7 @@ struct lpfc_vport {
#define FC_VPORT_CVL_RCVD 0x400000 /* VLink failed due to CVL */
#define FC_VFI_REGISTERED 0x800000 /* VFI is registered */
#define FC_FDISC_COMPLETED 0x1000000/* FDISC completed */
#define FC_DISC_DELAYED 0x2000000/* Delay NPort discovery */
uint32_t ct_flags;
#define FC_CT_RFF_ID 0x1 /* RFF_ID accepted by switch */
@ -348,6 +349,8 @@ struct lpfc_vport {
uint32_t fc_myDID; /* fibre channel S_ID */
uint32_t fc_prevDID; /* previous fibre channel S_ID */
struct lpfc_name fabric_portname;
struct lpfc_name fabric_nodename;
int32_t stopped; /* HBA has not been restarted since last ERATT */
uint8_t fc_linkspeed; /* Link speed after last READ_LA */
@ -372,6 +375,7 @@ struct lpfc_vport {
#define WORKER_DISC_TMO 0x1 /* vport: Discovery timeout */
#define WORKER_ELS_TMO 0x2 /* vport: ELS timeout */
#define WORKER_FDMI_TMO 0x4 /* vport: FDMI timeout */
#define WORKER_DELAYED_DISC_TMO 0x8 /* vport: delayed discovery */
#define WORKER_MBOX_TMO 0x100 /* hba: MBOX timeout */
#define WORKER_HB_TMO 0x200 /* hba: Heart beat timeout */
@ -382,6 +386,7 @@ struct lpfc_vport {
struct timer_list fc_fdmitmo;
struct timer_list els_tmofunc;
struct timer_list delayed_disc_tmo;
int unreg_vpi_cmpl;
@ -548,6 +553,8 @@ struct lpfc_hba {
#define LPFC_SLI3_CRP_ENABLED 0x08
#define LPFC_SLI3_BG_ENABLED 0x20
#define LPFC_SLI3_DSS_ENABLED 0x40
#define LPFC_SLI4_PERFH_ENABLED 0x80
#define LPFC_SLI4_PHWQ_ENABLED 0x100
uint32_t iocb_cmd_size;
uint32_t iocb_rsp_size;
@ -655,7 +662,7 @@ struct lpfc_hba {
#define LPFC_INITIALIZE_LINK 0 /* do normal init_link mbox */
#define LPFC_DELAY_INIT_LINK 1 /* layered driver hold off */
#define LPFC_DELAY_INIT_LINK_INDEFINITELY 2 /* wait, manual intervention */
uint32_t cfg_enable_dss;
lpfc_vpd_t vpd; /* vital product data */
struct pci_dev *pcidev;
@ -792,6 +799,10 @@ struct lpfc_hba {
struct dentry *debug_slow_ring_trc;
struct lpfc_debugfs_trc *slow_ring_trc;
atomic_t slow_ring_trc_cnt;
/* iDiag debugfs sub-directory */
struct dentry *idiag_root;
struct dentry *idiag_pci_cfg;
struct dentry *idiag_que_info;
#endif
/* Used for deferred freeing of ELS data buffers */

View file

@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
* Copyright (C) 2004-2009 Emulex. All rights reserved. *
* Copyright (C) 2004-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@ -623,10 +623,14 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
int status = 0;
int cnt = 0;
int i;
int rc;
init_completion(&online_compl);
lpfc_workq_post_event(phba, &status, &online_compl,
rc = lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_OFFLINE_PREP);
if (rc == 0)
return -ENOMEM;
wait_for_completion(&online_compl);
if (status != 0)
@ -652,7 +656,10 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
}
init_completion(&online_compl);
lpfc_workq_post_event(phba, &status, &online_compl, type);
rc = lpfc_workq_post_event(phba, &status, &online_compl, type);
if (rc == 0)
return -ENOMEM;
wait_for_completion(&online_compl);
if (status != 0)
@ -671,6 +678,7 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type)
*
* Notes:
* Assumes any error from lpfc_do_offline() will be negative.
* Do not make this function static.
*
* Returns:
* lpfc_do_offline() return code if not zero
@ -682,6 +690,7 @@ lpfc_selective_reset(struct lpfc_hba *phba)
{
struct completion online_compl;
int status = 0;
int rc;
if (!phba->cfg_enable_hba_reset)
return -EIO;
@ -692,8 +701,11 @@ lpfc_selective_reset(struct lpfc_hba *phba)
return status;
init_completion(&online_compl);
lpfc_workq_post_event(phba, &status, &online_compl,
rc = lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_ONLINE);
if (rc == 0)
return -ENOMEM;
wait_for_completion(&online_compl);
if (status != 0)
@ -812,14 +824,17 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr,
struct lpfc_hba *phba = vport->phba;
struct completion online_compl;
int status=0;
int rc;
if (!phba->cfg_enable_hba_reset)
return -EACCES;
init_completion(&online_compl);
if(strncmp(buf, "online", sizeof("online") - 1) == 0) {
lpfc_workq_post_event(phba, &status, &online_compl,
rc = lpfc_workq_post_event(phba, &status, &online_compl,
LPFC_EVT_ONLINE);
if (rc == 0)
return -ENOMEM;
wait_for_completion(&online_compl);
} else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
@ -1278,6 +1293,28 @@ lpfc_fips_rev_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, PAGE_SIZE, "%d\n", phba->fips_spec_rev);
}
/**
* lpfc_dss_show - Return the current state of dss and the configured state
* @dev: class converted to a Scsi_host structure.
* @attr: device attribute, not used.
* @buf: on return contains the formatted text.
*
* Returns: size of formatted string.
**/
static ssize_t
lpfc_dss_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct Scsi_Host *shost = class_to_shost(dev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
return snprintf(buf, PAGE_SIZE, "%s - %sOperational\n",
(phba->cfg_enable_dss) ? "Enabled" : "Disabled",
(phba->sli3_options & LPFC_SLI3_DSS_ENABLED) ?
"" : "Not ");
}
/**
* lpfc_param_show - Return a cfg attribute value in decimal
*
@ -1597,13 +1634,13 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \
#define LPFC_ATTR(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_init(name, defval, minval, maxval)
#define LPFC_ATTR_R(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_show(name)\
lpfc_param_init(name, defval, minval, maxval)\
@ -1611,7 +1648,7 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
#define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_show(name)\
lpfc_param_init(name, defval, minval, maxval)\
@ -1622,7 +1659,7 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
#define LPFC_ATTR_HEX_R(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_hex_show(name)\
lpfc_param_init(name, defval, minval, maxval)\
@ -1630,7 +1667,7 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
#define LPFC_ATTR_HEX_RW(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_param_hex_show(name)\
lpfc_param_init(name, defval, minval, maxval)\
@ -1641,13 +1678,13 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
#define LPFC_VPORT_ATTR(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_init(name, defval, minval, maxval)
#define LPFC_VPORT_ATTR_R(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_show(name)\
lpfc_vport_param_init(name, defval, minval, maxval)\
@ -1655,7 +1692,7 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
#define LPFC_VPORT_ATTR_RW(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_show(name)\
lpfc_vport_param_init(name, defval, minval, maxval)\
@ -1666,7 +1703,7 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
#define LPFC_VPORT_ATTR_HEX_R(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_hex_show(name)\
lpfc_vport_param_init(name, defval, minval, maxval)\
@ -1674,7 +1711,7 @@ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
#define LPFC_VPORT_ATTR_HEX_RW(name, defval, minval, maxval, desc) \
static uint lpfc_##name = defval;\
module_param(lpfc_##name, uint, 0);\
module_param(lpfc_##name, uint, S_IRUGO);\
MODULE_PARM_DESC(lpfc_##name, desc);\
lpfc_vport_param_hex_show(name)\
lpfc_vport_param_init(name, defval, minval, maxval)\
@ -1718,7 +1755,7 @@ static DEVICE_ATTR(npiv_info, S_IRUGO, lpfc_npiv_info_show, NULL);
static DEVICE_ATTR(lpfc_temp_sensor, S_IRUGO, lpfc_temp_sensor_show, NULL);
static DEVICE_ATTR(lpfc_fips_level, S_IRUGO, lpfc_fips_level_show, NULL);
static DEVICE_ATTR(lpfc_fips_rev, S_IRUGO, lpfc_fips_rev_show, NULL);
static DEVICE_ATTR(lpfc_dss, S_IRUGO, lpfc_dss_show, NULL);
static char *lpfc_soft_wwn_key = "C99G71SL8032A";
@ -1813,6 +1850,7 @@ lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr,
int stat1=0, stat2=0;
unsigned int i, j, cnt=count;
u8 wwpn[8];
int rc;
if (!phba->cfg_enable_hba_reset)
return -EACCES;
@ -1863,7 +1901,11 @@ lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr,
"0463 lpfc_soft_wwpn attribute set failed to "
"reinit adapter - %d\n", stat1);
init_completion(&online_compl);
lpfc_workq_post_event(phba, &stat2, &online_compl, LPFC_EVT_ONLINE);
rc = lpfc_workq_post_event(phba, &stat2, &online_compl,
LPFC_EVT_ONLINE);
if (rc == 0)
return -ENOMEM;
wait_for_completion(&online_compl);
if (stat2)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@ -1954,7 +1996,7 @@ static DEVICE_ATTR(lpfc_soft_wwnn, S_IRUGO | S_IWUSR,\
static int lpfc_poll = 0;
module_param(lpfc_poll, int, 0);
module_param(lpfc_poll, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:"
" 0 - none,"
" 1 - poll with interrupts enabled"
@ -1964,21 +2006,21 @@ static DEVICE_ATTR(lpfc_poll, S_IRUGO | S_IWUSR,
lpfc_poll_show, lpfc_poll_store);
int lpfc_sli_mode = 0;
module_param(lpfc_sli_mode, int, 0);
module_param(lpfc_sli_mode, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_sli_mode, "SLI mode selector:"
" 0 - auto (SLI-3 if supported),"
" 2 - select SLI-2 even on SLI-3 capable HBAs,"
" 3 - select SLI-3");
int lpfc_enable_npiv = 1;
module_param(lpfc_enable_npiv, int, 0);
module_param(lpfc_enable_npiv, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_enable_npiv, "Enable NPIV functionality");
lpfc_param_show(enable_npiv);
lpfc_param_init(enable_npiv, 1, 0, 1);
static DEVICE_ATTR(lpfc_enable_npiv, S_IRUGO, lpfc_enable_npiv_show, NULL);
int lpfc_enable_rrq;
module_param(lpfc_enable_rrq, int, 0);
module_param(lpfc_enable_rrq, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_enable_rrq, "Enable RRQ functionality");
lpfc_param_show(enable_rrq);
lpfc_param_init(enable_rrq, 0, 0, 1);
@ -2040,7 +2082,7 @@ static DEVICE_ATTR(txcmplq_hw, S_IRUGO,
lpfc_txcmplq_hw_show, NULL);
int lpfc_iocb_cnt = 2;
module_param(lpfc_iocb_cnt, int, 1);
module_param(lpfc_iocb_cnt, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_iocb_cnt,
"Number of IOCBs alloc for ELS, CT, and ABTS: 1k to 5k IOCBs");
lpfc_param_show(iocb_cnt);
@ -2192,7 +2234,7 @@ static DEVICE_ATTR(lpfc_nodev_tmo, S_IRUGO | S_IWUSR,
# disappear until the timer expires. Value range is [0,255]. Default
# value is 30.
*/
module_param(lpfc_devloss_tmo, int, 0);
module_param(lpfc_devloss_tmo, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_devloss_tmo,
"Seconds driver will hold I/O waiting "
"for a device to come back");
@ -2302,7 +2344,7 @@ LPFC_VPORT_ATTR_R(peer_port_login, 0, 0, 1,
# Default value of this parameter is 1.
*/
static int lpfc_restrict_login = 1;
module_param(lpfc_restrict_login, int, 0);
module_param(lpfc_restrict_login, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_restrict_login,
"Restrict virtual ports login to remote initiators.");
lpfc_vport_param_show(restrict_login);
@ -2473,7 +2515,7 @@ lpfc_topology_store(struct device *dev, struct device_attribute *attr,
return -EINVAL;
}
static int lpfc_topology = 0;
module_param(lpfc_topology, int, 0);
module_param(lpfc_topology, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_topology, "Select Fibre Channel topology");
lpfc_param_show(topology)
lpfc_param_init(topology, 0, 0, 6)
@ -2915,7 +2957,7 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr,
}
static int lpfc_link_speed = 0;
module_param(lpfc_link_speed, int, 0);
module_param(lpfc_link_speed, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_link_speed, "Select link speed");
lpfc_param_show(link_speed)
@ -3043,7 +3085,7 @@ lpfc_aer_support_store(struct device *dev, struct device_attribute *attr,
}
static int lpfc_aer_support = 1;
module_param(lpfc_aer_support, int, 1);
module_param(lpfc_aer_support, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_aer_support, "Enable PCIe device AER support");
lpfc_param_show(aer_support)
@ -3155,7 +3197,7 @@ LPFC_VPORT_ATTR_RW(use_adisc, 0, 0, 1,
# The value is set in milliseconds.
*/
static int lpfc_max_scsicmpl_time;
module_param(lpfc_max_scsicmpl_time, int, 0);
module_param(lpfc_max_scsicmpl_time, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_max_scsicmpl_time,
"Use command completion time to control queue depth");
lpfc_vport_param_show(max_scsicmpl_time);
@ -3331,7 +3373,7 @@ LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
*/
unsigned int lpfc_prot_mask = SHOST_DIF_TYPE1_PROTECTION;
module_param(lpfc_prot_mask, uint, 0);
module_param(lpfc_prot_mask, uint, S_IRUGO);
MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
/*
@ -3343,9 +3385,28 @@ MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
#
*/
unsigned char lpfc_prot_guard = SHOST_DIX_GUARD_IP;
module_param(lpfc_prot_guard, byte, 0);
module_param(lpfc_prot_guard, byte, S_IRUGO);
MODULE_PARM_DESC(lpfc_prot_guard, "host protection guard type");
/*
* Delay initial NPort discovery when Clean Address bit is cleared in
* FLOGI/FDISC accept and FCID/Fabric name/Fabric portname is changed.
* This parameter can have value 0 or 1.
* When this parameter is set to 0, no delay is added to the initial
* discovery.
* When this parameter is set to non-zero value, initial Nport discovery is
* delayed by ra_tov seconds when Clean Address bit is cleared in FLOGI/FDISC
* accept and FCID/Fabric name/Fabric portname is changed.
* Driver always delay Nport discovery for subsequent FLOGI/FDISC completion
* when Clean Address bit is cleared in FLOGI/FDISC
* accept and FCID/Fabric name/Fabric portname is changed.
* Default value is 0.
*/
int lpfc_delay_discovery;
module_param(lpfc_delay_discovery, int, S_IRUGO);
MODULE_PARM_DESC(lpfc_delay_discovery,
"Delay NPort discovery when Clean Address bit is cleared. "
"Allowed values: 0,1.");
/*
* lpfc_sg_seg_cnt - Initial Maximum DMA Segment Count
@ -3437,6 +3498,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_txcmplq_hw,
&dev_attr_lpfc_fips_level,
&dev_attr_lpfc_fips_rev,
&dev_attr_lpfc_dss,
NULL,
};
@ -4639,6 +4701,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_aer_support_init(phba, lpfc_aer_support);
lpfc_suppress_link_up_init(phba, lpfc_suppress_link_up);
lpfc_iocb_cnt_init(phba, lpfc_iocb_cnt);
phba->cfg_enable_dss = 1;
return;
}

View file

@ -53,9 +53,9 @@ void lpfc_unreg_vpi(struct lpfc_hba *, uint16_t, LPFC_MBOXQ_t *);
void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *);
void lpfc_supported_pages(struct lpfcMboxq *);
void lpfc_sli4_params(struct lpfcMboxq *);
void lpfc_pc_sli4_params(struct lpfcMboxq *);
int lpfc_pc_sli4_params_get(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_get_sli4_parameters(struct lpfc_hba *, LPFC_MBOXQ_t *);
struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t);
void lpfc_cleanup_rcv_buffers(struct lpfc_vport *);
void lpfc_rcv_seq_check_edtov(struct lpfc_vport *);
@ -167,6 +167,8 @@ int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t);
int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int);
void lpfc_fdmi_tmo(unsigned long);
void lpfc_fdmi_timeout_handler(struct lpfc_vport *);
void lpfc_delayed_disc_tmo(unsigned long);
void lpfc_delayed_disc_timeout_handler(struct lpfc_vport *);
int lpfc_config_port_prep(struct lpfc_hba *);
int lpfc_config_port_post(struct lpfc_hba *);
@ -341,6 +343,7 @@ extern struct fc_function_template lpfc_transport_functions;
extern struct fc_function_template lpfc_vport_transport_functions;
extern int lpfc_sli_mode;
extern int lpfc_enable_npiv;
extern int lpfc_delay_discovery;
int lpfc_vport_symbolic_node_name(struct lpfc_vport *, char *, size_t);
int lpfc_vport_symbolic_port_name(struct lpfc_vport *, char *, size_t);
@ -423,6 +426,6 @@ int lpfc_send_rrq(struct lpfc_hba *, struct lpfc_node_rrq *);
int lpfc_set_rrq_active(struct lpfc_hba *, struct lpfc_nodelist *,
uint16_t, uint16_t, uint16_t);
void lpfc_cleanup_wt_rrqs(struct lpfc_hba *);
void lpfc_cleanup_vports_rrqs(struct lpfc_vport *);
void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *);
struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t,
uint32_t);

View file

@ -1738,6 +1738,55 @@ fdmi_cmd_exit:
return 1;
}
/**
* lpfc_delayed_disc_tmo - Timeout handler for delayed discovery timer.
* @ptr - Context object of the timer.
*
* This function set the WORKER_DELAYED_DISC_TMO flag and wake up
* the worker thread.
**/
void
lpfc_delayed_disc_tmo(unsigned long ptr)
{
struct lpfc_vport *vport = (struct lpfc_vport *)ptr;
struct lpfc_hba *phba = vport->phba;
uint32_t tmo_posted;
unsigned long iflag;
spin_lock_irqsave(&vport->work_port_lock, iflag);
tmo_posted = vport->work_port_events & WORKER_DELAYED_DISC_TMO;
if (!tmo_posted)
vport->work_port_events |= WORKER_DELAYED_DISC_TMO;
spin_unlock_irqrestore(&vport->work_port_lock, iflag);
if (!tmo_posted)
lpfc_worker_wake_up(phba);
return;
}
/**
* lpfc_delayed_disc_timeout_handler - Function called by worker thread to
* handle delayed discovery.
* @vport: pointer to a host virtual N_Port data structure.
*
* This function start nport discovery of the vport.
**/
void
lpfc_delayed_disc_timeout_handler(struct lpfc_vport *vport)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
spin_lock_irq(shost->host_lock);
if (!(vport->fc_flag & FC_DISC_DELAYED)) {
spin_unlock_irq(shost->host_lock);
return;
}
vport->fc_flag &= ~FC_DISC_DELAYED;
spin_unlock_irq(shost->host_lock);
lpfc_do_scr_ns_plogi(vport->phba, vport);
}
void
lpfc_fdmi_tmo(unsigned long ptr)
{

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
* Copyright (C) 2007 Emulex. All rights reserved. *
* Copyright (C) 2007-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@ -22,6 +22,44 @@
#define _H_LPFC_DEBUG_FS
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/* size of output line, for discovery_trace and slow_ring_trace */
#define LPFC_DEBUG_TRC_ENTRY_SIZE 100
/* nodelist output buffer size */
#define LPFC_NODELIST_SIZE 8192
#define LPFC_NODELIST_ENTRY_SIZE 120
/* dumpHBASlim output buffer size */
#define LPFC_DUMPHBASLIM_SIZE 4096
/* dumpHostSlim output buffer size */
#define LPFC_DUMPHOSTSLIM_SIZE 4096
/* hbqinfo output buffer size */
#define LPFC_HBQINFO_SIZE 8192
/* rdPciConf output buffer size */
#define LPFC_PCI_CFG_SIZE 4096
#define LPFC_PCI_CFG_RD_BUF_SIZE (LPFC_PCI_CFG_SIZE/2)
#define LPFC_PCI_CFG_RD_SIZE (LPFC_PCI_CFG_SIZE/4)
/* queue info output buffer size */
#define LPFC_QUE_INFO_GET_BUF_SIZE 2048
#define SIZE_U8 sizeof(uint8_t)
#define SIZE_U16 sizeof(uint16_t)
#define SIZE_U32 sizeof(uint32_t)
struct lpfc_debug {
char *i_private;
char op;
#define LPFC_IDIAG_OP_RD 1
#define LPFC_IDIAG_OP_WR 2
char *buffer;
int len;
};
struct lpfc_debugfs_trc {
char *fmt;
uint32_t data1;
@ -30,6 +68,26 @@ struct lpfc_debugfs_trc {
uint32_t seq_cnt;
unsigned long jif;
};
struct lpfc_idiag_offset {
uint32_t last_rd;
};
#define LPFC_IDIAG_CMD_DATA_SIZE 4
struct lpfc_idiag_cmd {
uint32_t opcode;
#define LPFC_IDIAG_CMD_PCICFG_RD 0x00000001
#define LPFC_IDIAG_CMD_PCICFG_WR 0x00000002
#define LPFC_IDIAG_CMD_PCICFG_ST 0x00000003
#define LPFC_IDIAG_CMD_PCICFG_CL 0x00000004
uint32_t data[LPFC_IDIAG_CMD_DATA_SIZE];
};
struct lpfc_idiag {
uint32_t active;
struct lpfc_idiag_cmd cmd;
struct lpfc_idiag_offset offset;
};
#endif
/* Mask for discovery_trace */

View file

@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
* Copyright (C) 2004-2009 Emulex. All rights reserved. *
* Copyright (C) 2004-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@ -484,6 +484,59 @@ fail:
return rc;
}
/**
* lpfc_check_clean_addr_bit - Check whether assigned FCID is clean.
* @vport: pointer to a host virtual N_Port data structure.
* @sp: pointer to service parameter data structure.
*
* This routine is called from FLOGI/FDISC completion handler functions.
* lpfc_check_clean_addr_bit return 1 when FCID/Fabric portname/ Fabric
* node nodename is changed in the completion service parameter else return
* 0. This function also set flag in the vport data structure to delay
* NP_Port discovery after the FLOGI/FDISC completion if Clean address bit
* in FLOGI/FDISC response is cleared and FCID/Fabric portname/ Fabric
* node nodename is changed in the completion service parameter.
*
* Return code
* 0 - FCID and Fabric Nodename and Fabric portname is not changed.
* 1 - FCID or Fabric Nodename or Fabric portname is changed.
*
**/
static uint8_t
lpfc_check_clean_addr_bit(struct lpfc_vport *vport,
struct serv_parm *sp)
{
uint8_t fabric_param_changed = 0;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
if ((vport->fc_prevDID != vport->fc_myDID) ||
memcmp(&vport->fabric_portname, &sp->portName,
sizeof(struct lpfc_name)) ||
memcmp(&vport->fabric_nodename, &sp->nodeName,
sizeof(struct lpfc_name)))
fabric_param_changed = 1;
/*
* Word 1 Bit 31 in common service parameter is overloaded.
* Word 1 Bit 31 in FLOGI request is multiple NPort request
* Word 1 Bit 31 in FLOGI response is clean address bit
*
* If fabric parameter is changed and clean address bit is
* cleared delay nport discovery if
* - vport->fc_prevDID != 0 (not initial discovery) OR
* - lpfc_delay_discovery module parameter is set.
*/
if (fabric_param_changed && !sp->cmn.clean_address_bit &&
(vport->fc_prevDID || lpfc_delay_discovery)) {
spin_lock_irq(shost->host_lock);
vport->fc_flag |= FC_DISC_DELAYED;
spin_unlock_irq(shost->host_lock);
}
return fabric_param_changed;
}
/**
* lpfc_cmpl_els_flogi_fabric - Completion function for flogi to a fabric port
* @vport: pointer to a host virtual N_Port data structure.
@ -512,6 +565,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *np;
struct lpfc_nodelist *next_np;
uint8_t fabric_param_changed;
spin_lock_irq(shost->host_lock);
vport->fc_flag |= FC_FABRIC;
@ -544,6 +598,12 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_class_sup |= FC_COS_CLASS4;
ndlp->nlp_maxframe = ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) |
sp->cmn.bbRcvSizeLsb;
fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp);
memcpy(&vport->fabric_portname, &sp->portName,
sizeof(struct lpfc_name));
memcpy(&vport->fabric_nodename, &sp->nodeName,
sizeof(struct lpfc_name));
memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
@ -565,7 +625,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
}
}
if ((vport->fc_prevDID != vport->fc_myDID) &&
if (fabric_param_changed &&
!(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
/* If our NportID changed, we need to ensure all
@ -2203,6 +2263,7 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
IOCB_t *irsp;
struct lpfc_sli *psli;
struct lpfcMboxq *mbox;
psli = &phba->sli;
/* we pass cmdiocb to state machine which needs rspiocb as well */
@ -2260,6 +2321,21 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
NLP_EVT_CMPL_LOGO);
out:
lpfc_els_free_iocb(phba, cmdiocb);
/* If we are in pt2pt mode, we could rcv new S_ID on PLOGI */
if ((vport->fc_flag & FC_PT2PT) &&
!(vport->fc_flag & FC_PT2PT_PLOGI)) {
phba->pport->fc_myDID = 0;
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (mbox) {
lpfc_config_link(phba, mbox);
mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
mbox->vport = vport;
if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) ==
MBX_NOT_FINISHED) {
mempool_free(mbox, phba->mbox_mem_pool);
}
}
}
return;
}
@ -2745,7 +2821,8 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
}
break;
case ELS_CMD_FDISC:
lpfc_issue_els_fdisc(vport, ndlp, retry);
if (!(vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI))
lpfc_issue_els_fdisc(vport, ndlp, retry);
break;
}
return;
@ -2815,9 +2892,17 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
switch (irsp->ulpStatus) {
case IOSTAT_FCP_RSP_ERROR:
case IOSTAT_REMOTE_STOP:
break;
case IOSTAT_REMOTE_STOP:
if (phba->sli_rev == LPFC_SLI_REV4) {
/* This IO was aborted by the target, we don't
* know the rxid and because we did not send the
* ABTS we cannot generate and RRQ.
*/
lpfc_set_rrq_active(phba, ndlp,
cmdiocb->sli4_xritag, 0, 0);
}
break;
case IOSTAT_LOCAL_REJECT:
switch ((irsp->un.ulpWord[4] & 0xff)) {
case IOERR_LOOP_OPEN_FAILURE:
@ -4013,28 +4098,34 @@ lpfc_els_clear_rrq(struct lpfc_vport *vport,
uint8_t *pcmd;
struct RRQ *rrq;
uint16_t rxid;
uint16_t xri;
struct lpfc_node_rrq *prrq;
pcmd = (uint8_t *) (((struct lpfc_dmabuf *) iocb->context2)->virt);
pcmd += sizeof(uint32_t);
rrq = (struct RRQ *)pcmd;
rxid = bf_get(rrq_oxid, rrq);
rrq->rrq_exchg = be32_to_cpu(rrq->rrq_exchg);
rxid = be16_to_cpu(bf_get(rrq_rxid, rrq));
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"2883 Clear RRQ for SID:x%x OXID:x%x RXID:x%x"
" x%x x%x\n",
bf_get(rrq_did, rrq),
bf_get(rrq_oxid, rrq),
be32_to_cpu(bf_get(rrq_did, rrq)),
be16_to_cpu(bf_get(rrq_oxid, rrq)),
rxid,
iocb->iotag, iocb->iocb.ulpContext);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
"Clear RRQ: did:x%x flg:x%x exchg:x%.08x",
ndlp->nlp_DID, ndlp->nlp_flag, rrq->rrq_exchg);
prrq = lpfc_get_active_rrq(vport, rxid, ndlp->nlp_DID);
if (vport->fc_myDID == be32_to_cpu(bf_get(rrq_did, rrq)))
xri = be16_to_cpu(bf_get(rrq_oxid, rrq));
else
xri = rxid;
prrq = lpfc_get_active_rrq(vport, xri, ndlp->nlp_DID);
if (prrq)
lpfc_clr_rrq_active(phba, rxid, prrq);
lpfc_clr_rrq_active(phba, xri, prrq);
return;
}
@ -6166,6 +6257,11 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (vport->load_flag & FC_UNLOADING)
goto dropit;
/* If NPort discovery is delayed drop incoming ELS */
if ((vport->fc_flag & FC_DISC_DELAYED) &&
(cmd != ELS_CMD_PLOGI))
goto dropit;
ndlp = lpfc_findnode_did(vport, did);
if (!ndlp) {
/* Cannot find existing Fabric ndlp, so allocate a new one */
@ -6218,6 +6314,12 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp);
lpfc_send_els_event(vport, ndlp, payload);
/* If Nport discovery is delayed, reject PLOGIs */
if (vport->fc_flag & FC_DISC_DELAYED) {
rjt_err = LSRJT_UNABLE_TPC;
break;
}
if (vport->port_state < LPFC_DISC_AUTH) {
if (!(phba->pport->fc_flag & FC_PT2PT) ||
(phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@ -6596,6 +6698,21 @@ void
lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
{
struct lpfc_nodelist *ndlp, *ndlp_fdmi;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
/*
* If lpfc_delay_discovery parameter is set and the clean address
* bit is cleared and fc fabric parameters chenged, delay FC NPort
* discovery.
*/
spin_lock_irq(shost->host_lock);
if (vport->fc_flag & FC_DISC_DELAYED) {
spin_unlock_irq(shost->host_lock);
mod_timer(&vport->delayed_disc_tmo,
jiffies + HZ * phba->fc_ratov);
return;
}
spin_unlock_irq(shost->host_lock);
ndlp = lpfc_findnode_did(vport, NameServer_DID);
if (!ndlp) {
@ -6938,6 +7055,9 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_nodelist *next_np;
IOCB_t *irsp = &rspiocb->iocb;
struct lpfc_iocbq *piocb;
struct lpfc_dmabuf *pcmd = cmdiocb->context2, *prsp;
struct serv_parm *sp;
uint8_t fabric_param_changed;
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0123 FDISC completes. x%x/x%x prevDID: x%x\n",
@ -6981,7 +7101,14 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
vport->fc_myDID = irsp->un.ulpWord[4] & Mask_DID;
lpfc_vport_set_state(vport, FC_VPORT_ACTIVE);
if ((vport->fc_prevDID != vport->fc_myDID) &&
prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
sp = prsp->virt + sizeof(uint32_t);
fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp);
memcpy(&vport->fabric_portname, &sp->portName,
sizeof(struct lpfc_name));
memcpy(&vport->fabric_nodename, &sp->nodeName,
sizeof(struct lpfc_name));
if (fabric_param_changed &&
!(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
/* If our NportID changed, we need to ensure all
* remaining NPORTs get unreg_login'ed so we can
@ -7581,6 +7708,32 @@ void lpfc_fabric_abort_hba(struct lpfc_hba *phba)
IOERR_SLI_ABORTED);
}
/**
* lpfc_sli4_vport_delete_els_xri_aborted -Remove all ndlp references for vport
* @vport: pointer to lpfc vport data structure.
*
* This routine is invoked by the vport cleanup for deletions and the cleanup
* for an ndlp on removal.
**/
void
lpfc_sli4_vport_delete_els_xri_aborted(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
unsigned long iflag = 0;
spin_lock_irqsave(&phba->hbalock, iflag);
spin_lock(&phba->sli4_hba.abts_sgl_list_lock);
list_for_each_entry_safe(sglq_entry, sglq_next,
&phba->sli4_hba.lpfc_abts_els_sgl_list, list) {
if (sglq_entry->ndlp && sglq_entry->ndlp->vport == vport)
sglq_entry->ndlp = NULL;
}
spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
return;
}
/**
* lpfc_sli4_els_xri_aborted - Slow-path process of els xri abort
* @phba: pointer to lpfc hba data structure.

View file

@ -658,6 +658,8 @@ lpfc_work_done(struct lpfc_hba *phba)
lpfc_ramp_down_queue_handler(phba);
if (work_port_events & WORKER_RAMP_UP_QUEUE)
lpfc_ramp_up_queue_handler(phba);
if (work_port_events & WORKER_DELAYED_DISC_TMO)
lpfc_delayed_disc_timeout_handler(vport);
}
lpfc_destroy_vport_work_array(phba, vports);
@ -838,6 +840,11 @@ lpfc_linkdown_port(struct lpfc_vport *vport)
lpfc_port_link_failure(vport);
/* Stop delayed Nport discovery */
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_DISC_DELAYED;
spin_unlock_irq(shost->host_lock);
del_timer_sync(&vport->delayed_disc_tmo);
}
int
@ -3160,7 +3167,7 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
spin_unlock_irq(shost->host_lock);
vport->unreg_vpi_cmpl = VPORT_OK;
mempool_free(pmb, phba->mbox_mem_pool);
lpfc_cleanup_vports_rrqs(vport);
lpfc_cleanup_vports_rrqs(vport, NULL);
/*
* This shost reference might have been taken at the beginning of
* lpfc_vport_delete()
@ -3900,6 +3907,8 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
return;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
if (vport->phba->sli_rev == LPFC_SLI_REV4)
lpfc_cleanup_vports_rrqs(vport, ndlp);
lpfc_nlp_put(ndlp);
return;
}
@ -4289,7 +4298,7 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
list_del_init(&ndlp->els_retry_evt.evt_listp);
list_del_init(&ndlp->dev_loss_evt.evt_listp);
lpfc_cleanup_vports_rrqs(vport, ndlp);
lpfc_unreg_rpi(vport, ndlp);
return 0;
@ -4426,10 +4435,11 @@ lpfc_findnode_did(struct lpfc_vport *vport, uint32_t did)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp;
unsigned long iflags;
spin_lock_irq(shost->host_lock);
spin_lock_irqsave(shost->host_lock, iflags);
ndlp = __lpfc_findnode_did(vport, did);
spin_unlock_irq(shost->host_lock);
spin_unlock_irqrestore(shost->host_lock, iflags);
return ndlp;
}

View file

@ -341,6 +341,12 @@ struct csp {
uint8_t bbCreditMsb;
uint8_t bbCreditlsb; /* FC Word 0, byte 3 */
/*
* Word 1 Bit 31 in common service parameter is overloaded.
* Word 1 Bit 31 in FLOGI request is multiple NPort request
* Word 1 Bit 31 in FLOGI response is clean address bit
*/
#define clean_address_bit request_multiple_Nport /* Word 1, bit 31 */
#ifdef __BIG_ENDIAN_BITFIELD
uint16_t request_multiple_Nport:1; /* FC Word 1, bit 31 */
uint16_t randomOffset:1; /* FC Word 1, bit 30 */
@ -3198,7 +3204,10 @@ typedef struct {
#define IOERR_SLER_RRQ_RJT_ERR 0x4C
#define IOERR_SLER_RRQ_RETRY_ERR 0x4D
#define IOERR_SLER_ABTS_ERR 0x4E
#define IOERR_ELXSEC_KEY_UNWRAP_ERROR 0xF0
#define IOERR_ELXSEC_KEY_UNWRAP_COMPARE_ERROR 0xF1
#define IOERR_ELXSEC_CRYPTO_ERROR 0xF2
#define IOERR_ELXSEC_CRYPTO_COMPARE_ERROR 0xF3
#define IOERR_DRVR_MASK 0x100
#define IOERR_SLI_DOWN 0x101 /* ulpStatus - Driver defined */
#define IOERR_SLI_BRESET 0x102

View file

@ -778,6 +778,7 @@ struct mbox_header {
#define LPFC_MBOX_OPCODE_QUERY_FW_CFG 0x3A
#define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D
#define LPFC_MBOX_OPCODE_MQ_CREATE_EXT 0x5A
#define LPFC_MBOX_OPCODE_GET_SLI4_PARAMETERS 0xB5
/* FCoE Opcodes */
#define LPFC_MBOX_OPCODE_FCOE_WQ_CREATE 0x01
@ -1852,6 +1853,9 @@ struct lpfc_mbx_request_features {
#define lpfc_mbx_rq_ftr_rq_ifip_SHIFT 7
#define lpfc_mbx_rq_ftr_rq_ifip_MASK 0x00000001
#define lpfc_mbx_rq_ftr_rq_ifip_WORD word2
#define lpfc_mbx_rq_ftr_rq_perfh_SHIFT 11
#define lpfc_mbx_rq_ftr_rq_perfh_MASK 0x00000001
#define lpfc_mbx_rq_ftr_rq_perfh_WORD word2
uint32_t word3;
#define lpfc_mbx_rq_ftr_rsp_iaab_SHIFT 0
#define lpfc_mbx_rq_ftr_rsp_iaab_MASK 0x00000001
@ -1877,6 +1881,9 @@ struct lpfc_mbx_request_features {
#define lpfc_mbx_rq_ftr_rsp_ifip_SHIFT 7
#define lpfc_mbx_rq_ftr_rsp_ifip_MASK 0x00000001
#define lpfc_mbx_rq_ftr_rsp_ifip_WORD word3
#define lpfc_mbx_rq_ftr_rsp_perfh_SHIFT 11
#define lpfc_mbx_rq_ftr_rsp_perfh_MASK 0x00000001
#define lpfc_mbx_rq_ftr_rsp_perfh_WORD word3
};
struct lpfc_mbx_supp_pages {
@ -1935,7 +1942,7 @@ struct lpfc_mbx_supp_pages {
#define LPFC_SLI4_PARAMETERS 2
};
struct lpfc_mbx_sli4_params {
struct lpfc_mbx_pc_sli4_params {
uint32_t word1;
#define qs_SHIFT 0
#define qs_MASK 0x00000001
@ -2051,6 +2058,88 @@ struct lpfc_mbx_sli4_params {
uint32_t rsvd_13_63[51];
};
struct lpfc_sli4_parameters {
uint32_t word0;
#define cfg_prot_type_SHIFT 0
#define cfg_prot_type_MASK 0x000000FF
#define cfg_prot_type_WORD word0
uint32_t word1;
#define cfg_ft_SHIFT 0
#define cfg_ft_MASK 0x00000001
#define cfg_ft_WORD word1
#define cfg_sli_rev_SHIFT 4
#define cfg_sli_rev_MASK 0x0000000f
#define cfg_sli_rev_WORD word1
#define cfg_sli_family_SHIFT 8
#define cfg_sli_family_MASK 0x0000000f
#define cfg_sli_family_WORD word1
#define cfg_if_type_SHIFT 12
#define cfg_if_type_MASK 0x0000000f
#define cfg_if_type_WORD word1
#define cfg_sli_hint_1_SHIFT 16
#define cfg_sli_hint_1_MASK 0x000000ff
#define cfg_sli_hint_1_WORD word1
#define cfg_sli_hint_2_SHIFT 24
#define cfg_sli_hint_2_MASK 0x0000001f
#define cfg_sli_hint_2_WORD word1
uint32_t word2;
uint32_t word3;
uint32_t word4;
#define cfg_cqv_SHIFT 14
#define cfg_cqv_MASK 0x00000003
#define cfg_cqv_WORD word4
uint32_t word5;
uint32_t word6;
#define cfg_mqv_SHIFT 14
#define cfg_mqv_MASK 0x00000003
#define cfg_mqv_WORD word6
uint32_t word7;
uint32_t word8;
#define cfg_wqv_SHIFT 14
#define cfg_wqv_MASK 0x00000003
#define cfg_wqv_WORD word8
uint32_t word9;
uint32_t word10;
#define cfg_rqv_SHIFT 14
#define cfg_rqv_MASK 0x00000003
#define cfg_rqv_WORD word10
uint32_t word11;
#define cfg_rq_db_window_SHIFT 28
#define cfg_rq_db_window_MASK 0x0000000f
#define cfg_rq_db_window_WORD word11
uint32_t word12;
#define cfg_fcoe_SHIFT 0
#define cfg_fcoe_MASK 0x00000001
#define cfg_fcoe_WORD word12
#define cfg_phwq_SHIFT 15
#define cfg_phwq_MASK 0x00000001
#define cfg_phwq_WORD word12
#define cfg_loopbk_scope_SHIFT 28
#define cfg_loopbk_scope_MASK 0x0000000f
#define cfg_loopbk_scope_WORD word12
uint32_t sge_supp_len;
uint32_t word14;
#define cfg_sgl_page_cnt_SHIFT 0
#define cfg_sgl_page_cnt_MASK 0x0000000f
#define cfg_sgl_page_cnt_WORD word14
#define cfg_sgl_page_size_SHIFT 8
#define cfg_sgl_page_size_MASK 0x000000ff
#define cfg_sgl_page_size_WORD word14
#define cfg_sgl_pp_align_SHIFT 16
#define cfg_sgl_pp_align_MASK 0x000000ff
#define cfg_sgl_pp_align_WORD word14
uint32_t word15;
uint32_t word16;
uint32_t word17;
uint32_t word18;
uint32_t word19;
};
struct lpfc_mbx_get_sli4_parameters {
struct mbox_header header;
struct lpfc_sli4_parameters sli4_parameters;
};
/* Mailbox Completion Queue Error Messages */
#define MB_CQE_STATUS_SUCCESS 0x0
#define MB_CQE_STATUS_INSUFFICIENT_PRIVILEGES 0x1
@ -2103,7 +2192,8 @@ struct lpfc_mqe {
struct lpfc_mbx_post_hdr_tmpl hdr_tmpl;
struct lpfc_mbx_query_fw_cfg query_fw_cfg;
struct lpfc_mbx_supp_pages supp_pages;
struct lpfc_mbx_sli4_params sli4_params;
struct lpfc_mbx_pc_sli4_params sli4_params;
struct lpfc_mbx_get_sli4_parameters get_sli4_parameters;
struct lpfc_mbx_nop nop;
} un;
};
@ -2381,6 +2471,10 @@ struct wqe_common {
#define wqe_wqes_SHIFT 15
#define wqe_wqes_MASK 0x00000001
#define wqe_wqes_WORD word10
/* Note that this field overlaps above fields */
#define wqe_wqid_SHIFT 1
#define wqe_wqid_MASK 0x0000007f
#define wqe_wqid_WORD word10
#define wqe_pri_SHIFT 16
#define wqe_pri_MASK 0x00000007
#define wqe_pri_WORD word10
@ -2599,7 +2693,8 @@ struct fcp_iwrite64_wqe {
uint32_t total_xfer_len;
uint32_t initial_xfer_len;
struct wqe_common wqe_com; /* words 6-11 */
uint32_t rsvd_12_15[4]; /* word 12-15 */
uint32_t rsrvd12;
struct ulp_bde64 ph_bde; /* words 13-15 */
};
struct fcp_iread64_wqe {
@ -2608,7 +2703,8 @@ struct fcp_iread64_wqe {
uint32_t total_xfer_len; /* word 4 */
uint32_t rsrvd5; /* word 5 */
struct wqe_common wqe_com; /* words 6-11 */
uint32_t rsvd_12_15[4]; /* word 12-15 */
uint32_t rsrvd12;
struct ulp_bde64 ph_bde; /* words 13-15 */
};
struct fcp_icmnd64_wqe {

View file

@ -460,7 +460,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
|| ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_16G)
&& !(phba->lmt & LMT_16Gb))) {
/* Reset link speed to auto */
lpfc_printf_log(phba, KERN_WARNING, LOG_LINK_EVENT,
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
"1302 Invalid speed for this board: "
"Reset link speed to auto: x%x\n",
phba->cfg_link_speed);
@ -945,17 +945,13 @@ static void
lpfc_rrq_timeout(unsigned long ptr)
{
struct lpfc_hba *phba;
uint32_t tmo_posted;
unsigned long iflag;
phba = (struct lpfc_hba *)ptr;
spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
tmo_posted = phba->hba_flag & HBA_RRQ_ACTIVE;
if (!tmo_posted)
phba->hba_flag |= HBA_RRQ_ACTIVE;
phba->hba_flag |= HBA_RRQ_ACTIVE;
spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
if (!tmo_posted)
lpfc_worker_wake_up(phba);
lpfc_worker_wake_up(phba);
}
/**
@ -2280,6 +2276,7 @@ lpfc_cleanup(struct lpfc_vport *vport)
/* Wait for any activity on ndlps to settle */
msleep(10);
}
lpfc_cleanup_vports_rrqs(vport, NULL);
}
/**
@ -2295,6 +2292,7 @@ lpfc_stop_vport_timers(struct lpfc_vport *vport)
{
del_timer_sync(&vport->els_tmofunc);
del_timer_sync(&vport->fc_fdmitmo);
del_timer_sync(&vport->delayed_disc_tmo);
lpfc_can_disctmo(vport);
return;
}
@ -2355,6 +2353,10 @@ lpfc_stop_hba_timers(struct lpfc_hba *phba)
del_timer_sync(&phba->fabric_block_timer);
del_timer_sync(&phba->eratt_poll);
del_timer_sync(&phba->hb_tmofunc);
if (phba->sli_rev == LPFC_SLI_REV4) {
del_timer_sync(&phba->rrq_tmr);
phba->hba_flag &= ~HBA_RRQ_ACTIVE;
}
phba->hb_outstanding = 0;
switch (phba->pci_dev_grp) {
@ -2732,6 +2734,11 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
init_timer(&vport->els_tmofunc);
vport->els_tmofunc.function = lpfc_els_timeout;
vport->els_tmofunc.data = (unsigned long)vport;
init_timer(&vport->delayed_disc_tmo);
vport->delayed_disc_tmo.function = lpfc_delayed_disc_tmo;
vport->delayed_disc_tmo.data = (unsigned long)vport;
error = scsi_add_host_with_dma(shost, dev, &phba->pcidev->dev);
if (error)
goto out_put_shost;
@ -4283,36 +4290,37 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_bsmbx;
}
/* Get the Supported Pages. It is always available. */
/* Get the Supported Pages if PORT_CAPABILITIES is supported by port. */
lpfc_supported_pages(mboxq);
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
if (unlikely(rc)) {
rc = -EIO;
mempool_free(mboxq, phba->mbox_mem_pool);
goto out_free_bsmbx;
}
mqe = &mboxq->u.mqe;
memcpy(&pn_page[0], ((uint8_t *)&mqe->un.supp_pages.word3),
LPFC_MAX_SUPPORTED_PAGES);
for (i = 0; i < LPFC_MAX_SUPPORTED_PAGES; i++) {
switch (pn_page[i]) {
case LPFC_SLI4_PARAMETERS:
phba->sli4_hba.pc_sli4_params.supported = 1;
break;
default:
break;
if (!rc) {
mqe = &mboxq->u.mqe;
memcpy(&pn_page[0], ((uint8_t *)&mqe->un.supp_pages.word3),
LPFC_MAX_SUPPORTED_PAGES);
for (i = 0; i < LPFC_MAX_SUPPORTED_PAGES; i++) {
switch (pn_page[i]) {
case LPFC_SLI4_PARAMETERS:
phba->sli4_hba.pc_sli4_params.supported = 1;
break;
default:
break;
}
}
/* Read the port's SLI4 Parameters capabilities if supported. */
if (phba->sli4_hba.pc_sli4_params.supported)
rc = lpfc_pc_sli4_params_get(phba, mboxq);
if (rc) {
mempool_free(mboxq, phba->mbox_mem_pool);
rc = -EIO;
goto out_free_bsmbx;
}
}
/* Read the port's SLI4 Parameters capabilities if supported. */
if (phba->sli4_hba.pc_sli4_params.supported)
rc = lpfc_pc_sli4_params_get(phba, mboxq);
/*
* Get sli4 parameters that override parameters from Port capabilities.
* If this call fails it is not a critical error so continue loading.
*/
lpfc_get_sli4_parameters(phba, mboxq);
mempool_free(mboxq, phba->mbox_mem_pool);
if (rc) {
rc = -EIO;
goto out_free_bsmbx;
}
/* Create all the SLI4 queues */
rc = lpfc_sli4_queue_create(phba);
if (rc)
@ -7810,7 +7818,7 @@ lpfc_pc_sli4_params_get(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
mqe = &mboxq->u.mqe;
/* Read the port's SLI4 Parameters port capabilities */
lpfc_sli4_params(mboxq);
lpfc_pc_sli4_params(mboxq);
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
else {
@ -7853,6 +7861,66 @@ lpfc_pc_sli4_params_get(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
return rc;
}
/**
* lpfc_get_sli4_parameters - Get the SLI4 Config PARAMETERS.
* @phba: Pointer to HBA context object.
* @mboxq: Pointer to the mailboxq memory for the mailbox command response.
*
* This function is called in the SLI4 code path to read the port's
* sli4 capabilities.
*
* This function may be be called from any context that can block-wait
* for the completion. The expectation is that this routine is called
* typically from probe_one or from the online routine.
**/
int
lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
{
int rc;
struct lpfc_mqe *mqe = &mboxq->u.mqe;
struct lpfc_pc_sli4_params *sli4_params;
int length;
struct lpfc_sli4_parameters *mbx_sli4_parameters;
/* Read the port's SLI4 Config Parameters */
length = (sizeof(struct lpfc_mbx_get_sli4_parameters) -
sizeof(struct lpfc_sli4_cfg_mhdr));
lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
LPFC_MBOX_OPCODE_GET_SLI4_PARAMETERS,
length, LPFC_SLI4_MBX_EMBED);
if (!phba->sli4_hba.intr_enable)
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
else
rc = lpfc_sli_issue_mbox_wait(phba, mboxq,
lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG));
if (unlikely(rc))
return rc;
sli4_params = &phba->sli4_hba.pc_sli4_params;
mbx_sli4_parameters = &mqe->un.get_sli4_parameters.sli4_parameters;
sli4_params->if_type = bf_get(cfg_if_type, mbx_sli4_parameters);
sli4_params->sli_rev = bf_get(cfg_sli_rev, mbx_sli4_parameters);
sli4_params->sli_family = bf_get(cfg_sli_family, mbx_sli4_parameters);
sli4_params->featurelevel_1 = bf_get(cfg_sli_hint_1,
mbx_sli4_parameters);
sli4_params->featurelevel_2 = bf_get(cfg_sli_hint_2,
mbx_sli4_parameters);
if (bf_get(cfg_phwq, mbx_sli4_parameters))
phba->sli3_options |= LPFC_SLI4_PHWQ_ENABLED;
else
phba->sli3_options &= ~LPFC_SLI4_PHWQ_ENABLED;
sli4_params->sge_supp_len = mbx_sli4_parameters->sge_supp_len;
sli4_params->loopbk_scope = bf_get(loopbk_scope, mbx_sli4_parameters);
sli4_params->cqv = bf_get(cfg_cqv, mbx_sli4_parameters);
sli4_params->mqv = bf_get(cfg_mqv, mbx_sli4_parameters);
sli4_params->wqv = bf_get(cfg_wqv, mbx_sli4_parameters);
sli4_params->rqv = bf_get(cfg_rqv, mbx_sli4_parameters);
sli4_params->sgl_pages_max = bf_get(cfg_sgl_page_cnt,
mbx_sli4_parameters);
sli4_params->sgl_pp_align = bf_get(cfg_sgl_pp_align,
mbx_sli4_parameters);
return 0;
}
/**
* lpfc_pci_probe_one_s3 - PCI probe func to reg SLI-3 device to PCI subsystem.
* @pdev: pointer to PCI device

View file

@ -1263,7 +1263,8 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
if (phba->sli_rev == LPFC_SLI_REV3 && phba->vpd.sli3Feat.cerbm) {
if (phba->cfg_enable_bg)
mb->un.varCfgPort.cbg = 1; /* configure BlockGuard */
mb->un.varCfgPort.cdss = 1; /* Configure Security */
if (phba->cfg_enable_dss)
mb->un.varCfgPort.cdss = 1; /* Configure Security */
mb->un.varCfgPort.cerbm = 1; /* Request HBQs */
mb->un.varCfgPort.ccrp = 1; /* Command Ring Polling */
mb->un.varCfgPort.max_hbq = lpfc_sli_hbq_count();
@ -1692,7 +1693,7 @@ lpfc_sli4_mbox_cmd_free(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
* @mbox: pointer to lpfc mbox command.
* @subsystem: The sli4 config sub mailbox subsystem.
* @opcode: The sli4 config sub mailbox command opcode.
* @length: Length of the sli4 config mailbox command.
* @length: Length of the sli4 config mailbox command (including sub-header).
*
* This routine sets up the header fields of SLI4 specific mailbox command
* for sending IOCTL command.
@ -1723,14 +1724,14 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
if (emb) {
/* Set up main header fields */
bf_set(lpfc_mbox_hdr_emb, &sli4_config->header.cfg_mhdr, 1);
sli4_config->header.cfg_mhdr.payload_length =
LPFC_MBX_CMD_HDR_LENGTH + length;
sli4_config->header.cfg_mhdr.payload_length = length;
/* Set up sub-header fields following main header */
bf_set(lpfc_mbox_hdr_opcode,
&sli4_config->header.cfg_shdr.request, opcode);
bf_set(lpfc_mbox_hdr_subsystem,
&sli4_config->header.cfg_shdr.request, subsystem);
sli4_config->header.cfg_shdr.request.request_length = length;
sli4_config->header.cfg_shdr.request.request_length =
length - LPFC_MBX_CMD_HDR_LENGTH;
return length;
}
@ -1902,6 +1903,7 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
/* Set up host requested features. */
bf_set(lpfc_mbx_rq_ftr_rq_fcpi, &mboxq->u.mqe.un.req_ftrs, 1);
bf_set(lpfc_mbx_rq_ftr_rq_perfh, &mboxq->u.mqe.un.req_ftrs, 1);
/* Enable DIF (block guard) only if configured to do so. */
if (phba->cfg_enable_bg)
@ -2159,17 +2161,16 @@ lpfc_supported_pages(struct lpfcMboxq *mbox)
}
/**
* lpfc_sli4_params - Initialize the PORT_CAPABILITIES SLI4 Params
* mailbox command.
* lpfc_pc_sli4_params - Initialize the PORT_CAPABILITIES SLI4 Params mbox cmd.
* @mbox: pointer to lpfc mbox command to initialize.
*
* The PORT_CAPABILITIES SLI4 parameters mailbox command is issued to
* retrieve the particular SLI4 features supported by the port.
**/
void
lpfc_sli4_params(struct lpfcMboxq *mbox)
lpfc_pc_sli4_params(struct lpfcMboxq *mbox)
{
struct lpfc_mbx_sli4_params *sli4_params;
struct lpfc_mbx_pc_sli4_params *sli4_params;
memset(mbox, 0, sizeof(*mbox));
sli4_params = &mbox->u.mqe.un.sli4_params;

View file

@ -350,7 +350,11 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_maxframe =
((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb;
/* no need to reg_login if we are already in one of these states */
/*
* Need to unreg_login if we are already in one of these states and
* change to NPR state. This will block the port until after the ACC
* completes and the reg_login is issued and completed.
*/
switch (ndlp->nlp_state) {
case NLP_STE_NPR_NODE:
if (!(ndlp->nlp_flag & NLP_NPR_ADISC))
@ -359,8 +363,9 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
case NLP_STE_PRLI_ISSUE:
case NLP_STE_UNMAPPED_NODE:
case NLP_STE_MAPPED_NODE:
lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL);
return 1;
lpfc_unreg_rpi(vport, ndlp);
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
}
if ((vport->fc_flag & FC_PT2PT) &&

View file

@ -608,6 +608,32 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
return bcnt;
}
/**
* lpfc_sli4_vport_delete_fcp_xri_aborted -Remove all ndlp references for vport
* @vport: pointer to lpfc vport data structure.
*
* This routine is invoked by the vport cleanup for deletions and the cleanup
* for an ndlp on removal.
**/
void
lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_scsi_buf *psb, *next_psb;
unsigned long iflag = 0;
spin_lock_irqsave(&phba->hbalock, iflag);
spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock);
list_for_each_entry_safe(psb, next_psb,
&phba->sli4_hba.lpfc_abts_scsi_buf_list, list) {
if (psb->rdata && psb->rdata->pnode
&& psb->rdata->pnode->vport == vport)
psb->rdata = NULL;
}
spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock);
spin_unlock_irqrestore(&phba->hbalock, iflag);
}
/**
* lpfc_sli4_fcp_xri_aborted - Fast-path process of fcp xri abort
* @phba: pointer to lpfc hba data structure.
@ -640,7 +666,11 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba,
psb->status = IOSTAT_SUCCESS;
spin_unlock(
&phba->sli4_hba.abts_scsi_buf_list_lock);
ndlp = psb->rdata->pnode;
if (psb->rdata && psb->rdata->pnode)
ndlp = psb->rdata->pnode;
else
ndlp = NULL;
rrq_empty = list_empty(&phba->active_rrq_list);
spin_unlock_irqrestore(&phba->hbalock, iflag);
if (ndlp)
@ -964,36 +994,29 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
static struct lpfc_scsi_buf*
lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
{
struct lpfc_scsi_buf *lpfc_cmd = NULL;
struct lpfc_scsi_buf *start_lpfc_cmd = NULL;
struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
struct lpfc_scsi_buf *lpfc_cmd ;
unsigned long iflag = 0;
int found = 0;
spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list);
spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
while (!found && lpfc_cmd) {
list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list,
list) {
if (lpfc_test_rrq_active(phba, ndlp,
lpfc_cmd->cur_iocbq.sli4_xritag)) {
lpfc_release_scsi_buf_s4(phba, lpfc_cmd);
spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag);
list_remove_head(scsi_buf_list, lpfc_cmd,
struct lpfc_scsi_buf, list);
spin_unlock_irqrestore(&phba->scsi_buf_list_lock,
iflag);
if (lpfc_cmd == start_lpfc_cmd) {
lpfc_cmd = NULL;
break;
} else
continue;
}
lpfc_cmd->cur_iocbq.sli4_xritag))
continue;
list_del(&lpfc_cmd->list);
found = 1;
lpfc_cmd->seg_cnt = 0;
lpfc_cmd->nonsg_phys = 0;
lpfc_cmd->prot_seg_cnt = 0;
break;
}
return lpfc_cmd;
spin_unlock_irqrestore(&phba->scsi_buf_list_lock,
iflag);
if (!found)
return NULL;
else
return lpfc_cmd;
}
/**
* lpfc_get_scsi_buf - Get a scsi buffer from lpfc_scsi_buf_list of the HBA
@ -1981,12 +2004,14 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
struct scatterlist *sgel = NULL;
struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl;
struct sli4_sge *first_data_sgl;
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
dma_addr_t physaddr;
uint32_t num_bde = 0;
uint32_t dma_len;
uint32_t dma_offset = 0;
int nseg;
struct ulp_bde64 *bde;
/*
* There are three possibilities here - use scatter-gather segment, use
@ -2011,7 +2036,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
bf_set(lpfc_sli4_sge_last, sgl, 0);
sgl->word2 = cpu_to_le32(sgl->word2);
sgl += 1;
first_data_sgl = sgl;
lpfc_cmd->seg_cnt = nseg;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9074 BLKGRD:"
@ -2047,6 +2072,17 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
dma_offset += dma_len;
sgl++;
}
/* setup the performance hint (first data BDE) if enabled */
if (phba->sli3_options & LPFC_SLI4_PERFH_ENABLED) {
bde = (struct ulp_bde64 *)
&(iocb_cmd->unsli3.sli3Words[5]);
bde->addrLow = first_data_sgl->addr_lo;
bde->addrHigh = first_data_sgl->addr_hi;
bde->tus.f.bdeSize =
le32_to_cpu(first_data_sgl->sge_len);
bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
bde->tus.w = cpu_to_le32(bde->tus.w);
}
} else {
sgl += 1;
/* clear the last flag in the fcp_rsp map entry */
@ -2471,6 +2507,16 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_worker_wake_up(phba);
break;
case IOSTAT_LOCAL_REJECT:
case IOSTAT_REMOTE_STOP:
if (lpfc_cmd->result == IOERR_ELXSEC_KEY_UNWRAP_ERROR ||
lpfc_cmd->result ==
IOERR_ELXSEC_KEY_UNWRAP_COMPARE_ERROR ||
lpfc_cmd->result == IOERR_ELXSEC_CRYPTO_ERROR ||
lpfc_cmd->result ==
IOERR_ELXSEC_CRYPTO_COMPARE_ERROR) {
cmd->result = ScsiResult(DID_NO_CONNECT, 0);
break;
}
if (lpfc_cmd->result == IOERR_INVALID_RPI ||
lpfc_cmd->result == IOERR_NO_RESOURCES ||
lpfc_cmd->result == IOERR_ABORT_REQUESTED ||
@ -2478,7 +2524,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
cmd->result = ScsiResult(DID_REQUEUE, 0);
break;
}
if ((lpfc_cmd->result == IOERR_RX_DMA_FAILED ||
lpfc_cmd->result == IOERR_TX_DMA_FAILED) &&
pIocbOut->iocb.unsli3.sli3_bg.bgstat) {
@ -2497,7 +2542,17 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
"on unprotected cmd\n");
}
}
if ((lpfc_cmd->status == IOSTAT_REMOTE_STOP)
&& (phba->sli_rev == LPFC_SLI_REV4)
&& (pnode && NLP_CHK_NODE_ACT(pnode))) {
/* This IO was aborted by the target, we don't
* know the rxid and because we did not send the
* ABTS we cannot generate and RRQ.
*/
lpfc_set_rrq_active(phba, pnode,
lpfc_cmd->cur_iocbq.sli4_xritag,
0, 0);
}
/* else: fall through */
default:
cmd->result = ScsiResult(DID_ERROR, 0);
@ -2508,9 +2563,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
|| (pnode->nlp_state != NLP_STE_MAPPED_NODE))
cmd->result = ScsiResult(DID_TRANSPORT_DISRUPTED,
SAM_STAT_BUSY);
} else {
} else
cmd->result = ScsiResult(DID_OK, 0);
}
if (cmd->result || lpfc_cmd->fcp_rsp->rspSnsLen) {
uint32_t *lp = (uint32_t *)cmd->sense_buffer;
@ -3004,11 +3058,11 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
* transport is still transitioning.
*/
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0);
cmnd->result = ScsiResult(DID_IMM_RETRY, 0);
goto out_fail_command;
}
if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth)
goto out_host_busy;
goto out_tgt_busy;
lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp);
if (lpfc_cmd == NULL) {
@ -3125,6 +3179,9 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
out_host_busy:
return SCSI_MLQUEUE_HOST_BUSY;
out_tgt_busy:
return SCSI_MLQUEUE_TARGET_BUSY;
out_fail_command:
done(cmnd);
return 0;

View file

@ -96,7 +96,8 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe *wqe)
/* set consumption flag every once in a while */
if (!((q->host_index + 1) % LPFC_RELEASE_NOTIFICATION_INTERVAL))
bf_set(wqe_wqec, &wqe->generic.wqe_com, 1);
if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED)
bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id);
lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size);
/* Update the host index before invoking device */
@ -534,15 +535,35 @@ __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
uint16_t adj_xri;
struct lpfc_node_rrq *rrq;
int empty;
uint32_t did = 0;
if (!ndlp)
return -EINVAL;
if (!phba->cfg_enable_rrq)
return -EINVAL;
if (phba->pport->load_flag & FC_UNLOADING) {
phba->hba_flag &= ~HBA_RRQ_ACTIVE;
goto out;
}
did = ndlp->nlp_DID;
/*
* set the active bit even if there is no mem available.
*/
adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
if (!ndlp)
return -EINVAL;
if (NLP_CHK_FREE_REQ(ndlp))
goto out;
if (ndlp->vport && (ndlp->vport->load_flag & FC_UNLOADING))
goto out;
if (test_and_set_bit(adj_xri, ndlp->active_rrqs.xri_bitmap))
return -EINVAL;
goto out;
rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL);
if (rrq) {
rrq->send_rrq = send_rrq;
@ -553,14 +574,7 @@ __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
rrq->vport = ndlp->vport;
rrq->rxid = rxid;
empty = list_empty(&phba->active_rrq_list);
if (phba->cfg_enable_rrq && send_rrq)
/*
* We need the xri before we can add this to the
* phba active rrq list.
*/
rrq->send_rrq = send_rrq;
else
rrq->send_rrq = 0;
rrq->send_rrq = send_rrq;
list_add_tail(&rrq->list, &phba->active_rrq_list);
if (!(phba->hba_flag & HBA_RRQ_ACTIVE)) {
phba->hba_flag |= HBA_RRQ_ACTIVE;
@ -569,40 +583,49 @@ __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
}
return 0;
}
return -ENOMEM;
out:
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"2921 Can't set rrq active xri:0x%x rxid:0x%x"
" DID:0x%x Send:%d\n",
xritag, rxid, did, send_rrq);
return -EINVAL;
}
/**
* __lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap.
* lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap.
* @phba: Pointer to HBA context object.
* @xritag: xri used in this exchange.
* @rrq: The RRQ to be cleared.
*
* This function is called with hbalock held. This function
**/
static void
__lpfc_clr_rrq_active(struct lpfc_hba *phba,
uint16_t xritag,
struct lpfc_node_rrq *rrq)
void
lpfc_clr_rrq_active(struct lpfc_hba *phba,
uint16_t xritag,
struct lpfc_node_rrq *rrq)
{
uint16_t adj_xri;
struct lpfc_nodelist *ndlp;
struct lpfc_nodelist *ndlp = NULL;
ndlp = lpfc_findnode_did(rrq->vport, rrq->nlp_DID);
if ((rrq->vport) && NLP_CHK_NODE_ACT(rrq->ndlp))
ndlp = lpfc_findnode_did(rrq->vport, rrq->nlp_DID);
/* The target DID could have been swapped (cable swap)
* we should use the ndlp from the findnode if it is
* available.
*/
if (!ndlp)
if ((!ndlp) && rrq->ndlp)
ndlp = rrq->ndlp;
if (!ndlp)
goto out;
adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
if (test_and_clear_bit(adj_xri, ndlp->active_rrqs.xri_bitmap)) {
rrq->send_rrq = 0;
rrq->xritag = 0;
rrq->rrq_stop_time = 0;
}
out:
mempool_free(rrq, phba->rrq_pool);
}
@ -627,34 +650,34 @@ lpfc_handle_rrq_active(struct lpfc_hba *phba)
struct lpfc_node_rrq *nextrrq;
unsigned long next_time;
unsigned long iflags;
LIST_HEAD(send_rrq);
spin_lock_irqsave(&phba->hbalock, iflags);
phba->hba_flag &= ~HBA_RRQ_ACTIVE;
next_time = jiffies + HZ * (phba->fc_ratov + 1);
list_for_each_entry_safe(rrq, nextrrq,
&phba->active_rrq_list, list) {
if (time_after(jiffies, rrq->rrq_stop_time)) {
list_del(&rrq->list);
if (!rrq->send_rrq)
/* this call will free the rrq */
__lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
else {
/* if we send the rrq then the completion handler
* will clear the bit in the xribitmap.
*/
spin_unlock_irqrestore(&phba->hbalock, iflags);
if (lpfc_send_rrq(phba, rrq)) {
lpfc_clr_rrq_active(phba, rrq->xritag,
rrq);
}
spin_lock_irqsave(&phba->hbalock, iflags);
}
} else if (time_before(rrq->rrq_stop_time, next_time))
&phba->active_rrq_list, list) {
if (time_after(jiffies, rrq->rrq_stop_time))
list_move(&rrq->list, &send_rrq);
else if (time_before(rrq->rrq_stop_time, next_time))
next_time = rrq->rrq_stop_time;
}
spin_unlock_irqrestore(&phba->hbalock, iflags);
if (!list_empty(&phba->active_rrq_list))
mod_timer(&phba->rrq_tmr, next_time);
list_for_each_entry_safe(rrq, nextrrq, &send_rrq, list) {
list_del(&rrq->list);
if (!rrq->send_rrq)
/* this call will free the rrq */
lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
else if (lpfc_send_rrq(phba, rrq)) {
/* if we send the rrq then the completion handler
* will clear the bit in the xribitmap.
*/
lpfc_clr_rrq_active(phba, rrq->xritag,
rrq);
}
}
}
/**
@ -692,29 +715,37 @@ lpfc_get_active_rrq(struct lpfc_vport *vport, uint16_t xri, uint32_t did)
/**
* lpfc_cleanup_vports_rrqs - Remove and clear the active RRQ for this vport.
* @vport: Pointer to vport context object.
*
* Remove all active RRQs for this vport from the phba->active_rrq_list and
* clear the rrq.
* @ndlp: Pointer to the lpfc_node_list structure.
* If ndlp is NULL Remove all active RRQs for this vport from the
* phba->active_rrq_list and clear the rrq.
* If ndlp is not NULL then only remove rrqs for this vport & this ndlp.
**/
void
lpfc_cleanup_vports_rrqs(struct lpfc_vport *vport)
lpfc_cleanup_vports_rrqs(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_node_rrq *rrq;
struct lpfc_node_rrq *nextrrq;
unsigned long iflags;
LIST_HEAD(rrq_list);
if (phba->sli_rev != LPFC_SLI_REV4)
return;
spin_lock_irqsave(&phba->hbalock, iflags);
list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
if (rrq->vport == vport) {
list_del(&rrq->list);
__lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
}
if (!ndlp) {
lpfc_sli4_vport_delete_els_xri_aborted(vport);
lpfc_sli4_vport_delete_fcp_xri_aborted(vport);
}
spin_lock_irqsave(&phba->hbalock, iflags);
list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list)
if ((rrq->vport == vport) && (!ndlp || rrq->ndlp == ndlp))
list_move(&rrq->list, &rrq_list);
spin_unlock_irqrestore(&phba->hbalock, iflags);
list_for_each_entry_safe(rrq, nextrrq, &rrq_list, list) {
list_del(&rrq->list);
lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
}
}
/**
@ -732,24 +763,27 @@ lpfc_cleanup_wt_rrqs(struct lpfc_hba *phba)
struct lpfc_node_rrq *nextrrq;
unsigned long next_time;
unsigned long iflags;
LIST_HEAD(rrq_list);
if (phba->sli_rev != LPFC_SLI_REV4)
return;
spin_lock_irqsave(&phba->hbalock, iflags);
phba->hba_flag &= ~HBA_RRQ_ACTIVE;
next_time = jiffies + HZ * (phba->fc_ratov * 2);
list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
list_del(&rrq->list);
__lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
}
list_splice_init(&phba->active_rrq_list, &rrq_list);
spin_unlock_irqrestore(&phba->hbalock, iflags);
list_for_each_entry_safe(rrq, nextrrq, &rrq_list, list) {
list_del(&rrq->list);
lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
}
if (!list_empty(&phba->active_rrq_list))
mod_timer(&phba->rrq_tmr, next_time);
}
/**
* __lpfc_test_rrq_active - Test RRQ bit in xri_bitmap.
* lpfc_test_rrq_active - Test RRQ bit in xri_bitmap.
* @phba: Pointer to HBA context object.
* @ndlp: Targets nodelist pointer for this exchange.
* @xritag the xri in the bitmap to test.
@ -758,8 +792,8 @@ lpfc_cleanup_wt_rrqs(struct lpfc_hba *phba)
* returns 0 = rrq not active for this xri
* 1 = rrq is valid for this xri.
**/
static int
__lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
int
lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
uint16_t xritag)
{
uint16_t adj_xri;
@ -801,52 +835,6 @@ lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
return ret;
}
/**
* lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap.
* @phba: Pointer to HBA context object.
* @xritag: xri used in this exchange.
* @rrq: The RRQ to be cleared.
*
* This function is takes the hbalock.
**/
void
lpfc_clr_rrq_active(struct lpfc_hba *phba,
uint16_t xritag,
struct lpfc_node_rrq *rrq)
{
unsigned long iflags;
spin_lock_irqsave(&phba->hbalock, iflags);
__lpfc_clr_rrq_active(phba, xritag, rrq);
spin_unlock_irqrestore(&phba->hbalock, iflags);
return;
}
/**
* lpfc_test_rrq_active - Test RRQ bit in xri_bitmap.
* @phba: Pointer to HBA context object.
* @ndlp: Targets nodelist pointer for this exchange.
* @xritag the xri in the bitmap to test.
*
* This function takes the hbalock.
* returns 0 = rrq not active for this xri
* 1 = rrq is valid for this xri.
**/
int
lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
uint16_t xritag)
{
int ret;
unsigned long iflags;
spin_lock_irqsave(&phba->hbalock, iflags);
ret = __lpfc_test_rrq_active(phba, ndlp, xritag);
spin_unlock_irqrestore(&phba->hbalock, iflags);
return ret;
}
/**
* __lpfc_sli_get_sglq - Allocates an iocb object from sgl pool
* @phba: Pointer to HBA context object.
@ -884,7 +872,7 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
return NULL;
adj_xri = sglq->sli4_xritag -
phba->sli4_hba.max_cfg_param.xri_base;
if (__lpfc_test_rrq_active(phba, ndlp, sglq->sli4_xritag)) {
if (lpfc_test_rrq_active(phba, ndlp, sglq->sli4_xritag)) {
/* This xri has an rrq outstanding for this DID.
* put it back in the list and get another xri.
*/
@ -969,7 +957,8 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
} else {
sglq->state = SGL_FREED;
sglq->ndlp = NULL;
list_add(&sglq->list, &phba->sli4_hba.lpfc_sgl_list);
list_add_tail(&sglq->list,
&phba->sli4_hba.lpfc_sgl_list);
/* Check if TXQ queue needs to be serviced */
if (pring->txq_cnt)
@ -4817,7 +4806,10 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
"0378 No support for fcpi mode.\n");
ftr_rsp++;
}
if (bf_get(lpfc_mbx_rq_ftr_rsp_perfh, &mqe->un.req_ftrs))
phba->sli3_options |= LPFC_SLI4_PERFH_ENABLED;
else
phba->sli3_options &= ~LPFC_SLI4_PERFH_ENABLED;
/*
* If the port cannot support the host's requested features
* then turn off the global config parameters to disable the
@ -5004,7 +4996,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
phba->link_state = LPFC_LINK_DOWN;
spin_unlock_irq(&phba->hbalock);
rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT);
if (phba->cfg_suppress_link_up == LPFC_INITIALIZE_LINK)
rc = phba->lpfc_hba_init_link(phba, MBX_NOWAIT);
out_unset_queue:
/* Unset all the queues set up in this routine when error out */
if (rc)
@ -10478,6 +10471,7 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
cq->type = type;
cq->subtype = subtype;
cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response);
cq->assoc_qid = eq->queue_id;
cq->host_index = 0;
cq->hba_index = 0;
@ -10672,6 +10666,7 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq,
goto out;
}
mq->type = LPFC_MQ;
mq->assoc_qid = cq->queue_id;
mq->subtype = subtype;
mq->host_index = 0;
mq->hba_index = 0;
@ -10759,6 +10754,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
goto out;
}
wq->type = LPFC_WQ;
wq->assoc_qid = cq->queue_id;
wq->subtype = subtype;
wq->host_index = 0;
wq->hba_index = 0;
@ -10876,6 +10872,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
goto out;
}
hrq->type = LPFC_HRQ;
hrq->assoc_qid = cq->queue_id;
hrq->subtype = subtype;
hrq->host_index = 0;
hrq->hba_index = 0;
@ -10936,6 +10933,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
goto out;
}
drq->type = LPFC_DRQ;
drq->assoc_qid = cq->queue_id;
drq->subtype = subtype;
drq->host_index = 0;
drq->hba_index = 0;
@ -11189,7 +11187,7 @@ lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq,
if (!mbox)
return -ENOMEM;
length = (sizeof(struct lpfc_mbx_rq_destroy) -
sizeof(struct mbox_header));
sizeof(struct lpfc_sli4_cfg_mhdr));
lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
LPFC_MBOX_OPCODE_FCOE_RQ_DESTROY,
length, LPFC_SLI4_MBX_EMBED);
@ -11279,7 +11277,7 @@ lpfc_sli4_post_sgl(struct lpfc_hba *phba,
lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES,
sizeof(struct lpfc_mbx_post_sgl_pages) -
sizeof(struct mbox_header), LPFC_SLI4_MBX_EMBED);
sizeof(struct lpfc_sli4_cfg_mhdr), LPFC_SLI4_MBX_EMBED);
post_sgl_pages = (struct lpfc_mbx_post_sgl_pages *)
&mbox->u.mqe.un.post_sgl_pages;
@ -12402,7 +12400,8 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page)
lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE,
sizeof(struct lpfc_mbx_post_hdr_tmpl) -
sizeof(struct mbox_header), LPFC_SLI4_MBX_EMBED);
sizeof(struct lpfc_sli4_cfg_mhdr),
LPFC_SLI4_MBX_EMBED);
bf_set(lpfc_mbx_post_hdr_tmpl_page_cnt,
hdr_tmpl, rpi_page->page_count);
bf_set(lpfc_mbx_post_hdr_tmpl_rpi_offset, hdr_tmpl,

View file

@ -125,9 +125,9 @@ struct lpfc_queue {
uint32_t entry_count; /* Number of entries to support on the queue */
uint32_t entry_size; /* Size of each queue entry. */
uint32_t queue_id; /* Queue ID assigned by the hardware */
uint32_t assoc_qid; /* Queue ID associated with, for CQ/WQ/MQ */
struct list_head page_list;
uint32_t page_count; /* Number of pages allocated for this queue */
uint32_t host_index; /* The host's index for putting or getting */
uint32_t hba_index; /* The last known hba index for get or put */
union sli4_qe qe[1]; /* array to index entries (must be last) */
@ -359,6 +359,10 @@ struct lpfc_pc_sli4_params {
uint32_t hdr_pp_align;
uint32_t sgl_pages_max;
uint32_t sgl_pp_align;
uint8_t cqv;
uint8_t mqv;
uint8_t wqv;
uint8_t rqv;
};
/* SLI4 HBA data structure entries */
@ -562,6 +566,8 @@ void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *,
struct sli4_wcqe_xri_aborted *);
void lpfc_sli4_els_xri_aborted(struct lpfc_hba *,
struct sli4_wcqe_xri_aborted *);
void lpfc_sli4_vport_delete_els_xri_aborted(struct lpfc_vport *);
void lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *);
int lpfc_sli4_brdreset(struct lpfc_hba *);
int lpfc_sli4_add_fcf_record(struct lpfc_hba *, struct fcf_record *);
void lpfc_sli_remove_dflt_fcf(struct lpfc_hba *);

View file

@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
* Copyright (C) 2004-2010 Emulex. All rights reserved. *
* Copyright (C) 2004-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
#define LPFC_DRIVER_VERSION "8.3.20"
#define LPFC_DRIVER_VERSION "8.3.21"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
#define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp"

Some files were not shown because too many files have changed in this diff Show more