mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
![James.Smart@Emulex.Com](/assets/img/avatar_default.png)
We recently went back to implement a board reset. When we perform the reset, we wanted to tear down the internal data structures and rebuild them. Unfortunately, when it came to the rport structure, things were odd. If we deleted them, the scsi targets and sdevs would be torn down. Not a good thing for a temporary reset. We could block the rports, but we either maintain the internal structures to keep the rport reference (perhaps even replicating what's in the transport), or we have to fatten the fc transport with new search routines to find the rport (and deal with a case of a dangling rport that the driver forgets). It dawned on me that we had actually reached this state incorrectly. When the fc transport first started, we did the block/unblock first, then added the rport interface. The purpose of block/unblock is to hide the temporary disappearance of the rport (e.g. being deleted, then readded). Why are we making the driver do the block/unblock ? We should be making the transport have only an rport add/delete, and the let the transport handle the block/unblock. So... This patch removes the existing fc_remote_port_block/unblock functions. It moves the block/unblock functionality into the fc_remote_port_add/delete functions. Updates for the lpfc driver are included. Qlogic driver updates are also enclosed, thanks to the contributions of Andrew Vasquez. [Note: the qla2xxx changes are relative to the scsi-misc-2.6 tree as of this morning - which does not include the recent patches sent by Andrew]. The zfcp driver does not use the block/unblock functions. One last comment: The resulting behavior feels very clean. The LLDD is concerned only with add/delete, which corresponds to the physical disappearance. However, the fact that the scsi target and sdevs are not immediately torn down after the LLDD calls delete causes an interesting scenario... the midlayer can call the xxx_slave_alloc and xxx_queuecommand functions with a sdev that is at the location the rport used to be. The driver must validate the device exists when it first enters these functions. In thinking about it, this has always been the case for the LLDD and these routines. The existing drivers already check for existence. However, this highlights that simple validation via data structure dereferencing needs to be watched. To deal with this, a new transport function, fc_remote_port_chkready() was created that LLDDs should call when they first enter these two routines. It validates the rport state, and returns a scsi result which could be returned. In addition to solving the above, it also creates consistent behavior from the LLDD's when the block and deletes are occuring. Rejections fixed up and Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
1428 lines
42 KiB
C
1428 lines
42 KiB
C
/*
|
|
* QLogic Fibre Channel HBA Driver
|
|
* Copyright (c) 2003-2005 QLogic Corporation
|
|
*
|
|
* See LICENSE.qla2xxx for copyright and licensing details.
|
|
*/
|
|
#include "qla_def.h"
|
|
|
|
#include <scsi/scsi_transport_fc.h>
|
|
|
|
/**
|
|
* IO descriptor handle definitions.
|
|
*
|
|
* Signature form:
|
|
*
|
|
* |31------28|27-------------------12|11-------0|
|
|
* | Type | Rolling Signature | Index |
|
|
* |----------|-----------------------|----------|
|
|
*
|
|
**/
|
|
|
|
#define HDL_TYPE_SCSI 0
|
|
#define HDL_TYPE_ASYNC_IOCB 0x0A
|
|
|
|
#define HDL_INDEX_BITS 12
|
|
#define HDL_ITER_BITS 16
|
|
#define HDL_TYPE_BITS 4
|
|
|
|
#define HDL_INDEX_MASK ((1UL << HDL_INDEX_BITS) - 1)
|
|
#define HDL_ITER_MASK ((1UL << HDL_ITER_BITS) - 1)
|
|
#define HDL_TYPE_MASK ((1UL << HDL_TYPE_BITS) - 1)
|
|
|
|
#define HDL_INDEX_SHIFT 0
|
|
#define HDL_ITER_SHIFT (HDL_INDEX_SHIFT + HDL_INDEX_BITS)
|
|
#define HDL_TYPE_SHIFT (HDL_ITER_SHIFT + HDL_ITER_BITS)
|
|
|
|
/* Local Prototypes. */
|
|
static inline uint32_t qla2x00_to_handle(uint16_t, uint16_t, uint16_t);
|
|
static inline uint16_t qla2x00_handle_to_idx(uint32_t);
|
|
static inline uint32_t qla2x00_iodesc_to_handle(struct io_descriptor *);
|
|
static inline struct io_descriptor *qla2x00_handle_to_iodesc(scsi_qla_host_t *,
|
|
uint32_t);
|
|
|
|
static inline struct io_descriptor *qla2x00_alloc_iodesc(scsi_qla_host_t *);
|
|
static inline void qla2x00_free_iodesc(struct io_descriptor *);
|
|
static inline void qla2x00_init_io_descriptors(scsi_qla_host_t *);
|
|
|
|
static void qla2x00_iodesc_timeout(unsigned long);
|
|
static inline void qla2x00_add_iodesc_timer(struct io_descriptor *);
|
|
static inline void qla2x00_remove_iodesc_timer(struct io_descriptor *);
|
|
|
|
static inline void qla2x00_update_login_fcport(scsi_qla_host_t *,
|
|
struct mbx_entry *, fc_port_t *);
|
|
|
|
static int qla2x00_send_abort_iocb(scsi_qla_host_t *, struct io_descriptor *,
|
|
uint32_t, int);
|
|
static int qla2x00_send_abort_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
|
|
struct mbx_entry *);
|
|
|
|
static int qla2x00_send_adisc_iocb(scsi_qla_host_t *, struct io_descriptor *,
|
|
int);
|
|
static int qla2x00_send_adisc_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
|
|
struct mbx_entry *);
|
|
|
|
static int qla2x00_send_logout_iocb(scsi_qla_host_t *, struct io_descriptor *,
|
|
int);
|
|
static int qla2x00_send_logout_iocb_cb(scsi_qla_host_t *,
|
|
struct io_descriptor *, struct mbx_entry *);
|
|
|
|
static int qla2x00_send_login_iocb(scsi_qla_host_t *, struct io_descriptor *,
|
|
port_id_t *, int);
|
|
static int qla2x00_send_login_iocb_cb(scsi_qla_host_t *, struct io_descriptor *,
|
|
struct mbx_entry *);
|
|
|
|
/**
|
|
* Mailbox IOCB callback array.
|
|
**/
|
|
static int (*iocb_function_cb_list[LAST_IOCB_CB])
|
|
(scsi_qla_host_t *, struct io_descriptor *, struct mbx_entry *) = {
|
|
|
|
qla2x00_send_abort_iocb_cb,
|
|
qla2x00_send_adisc_iocb_cb,
|
|
qla2x00_send_logout_iocb_cb,
|
|
qla2x00_send_login_iocb_cb,
|
|
};
|
|
|
|
|
|
/**
|
|
* Generic IO descriptor handle routines.
|
|
**/
|
|
|
|
/**
|
|
* qla2x00_to_handle() - Create a descriptor handle.
|
|
* @type: descriptor type
|
|
* @iter: descriptor rolling signature
|
|
* @idx: index to the descriptor array
|
|
*
|
|
* Returns a composite handle based in the @type, @iter, and @idx.
|
|
*/
|
|
static inline uint32_t
|
|
qla2x00_to_handle(uint16_t type, uint16_t iter, uint16_t idx)
|
|
{
|
|
return ((uint32_t)(((uint32_t)type << HDL_TYPE_SHIFT) |
|
|
((uint32_t)iter << HDL_ITER_SHIFT) |
|
|
((uint32_t)idx << HDL_INDEX_SHIFT)));
|
|
}
|
|
|
|
/**
|
|
* qla2x00_handle_to_idx() - Retrive the index for a given handle.
|
|
* @handle: descriptor handle
|
|
*
|
|
* Returns the index specified by the @handle.
|
|
*/
|
|
static inline uint16_t
|
|
qla2x00_handle_to_idx(uint32_t handle)
|
|
{
|
|
return ((uint16_t)(((handle) >> HDL_INDEX_SHIFT) & HDL_INDEX_MASK));
|
|
}
|
|
|
|
/**
|
|
* qla2x00_iodesc_to_handle() - Convert an IO descriptor to a unique handle.
|
|
* @iodesc: io descriptor
|
|
*
|
|
* Returns a unique handle for @iodesc.
|
|
*/
|
|
static inline uint32_t
|
|
qla2x00_iodesc_to_handle(struct io_descriptor *iodesc)
|
|
{
|
|
uint32_t handle;
|
|
|
|
handle = qla2x00_to_handle(HDL_TYPE_ASYNC_IOCB,
|
|
++iodesc->ha->iodesc_signature, iodesc->idx);
|
|
iodesc->signature = handle;
|
|
|
|
return (handle);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_handle_to_iodesc() - Retrieve an IO descriptor given a unique handle.
|
|
* @ha: HA context
|
|
* @handle: handle to io descriptor
|
|
*
|
|
* Returns a pointer to the io descriptor, or NULL, if the io descriptor does
|
|
* not exist or the io descriptors signature does not @handle.
|
|
*/
|
|
static inline struct io_descriptor *
|
|
qla2x00_handle_to_iodesc(scsi_qla_host_t *ha, uint32_t handle)
|
|
{
|
|
uint16_t idx;
|
|
struct io_descriptor *iodesc;
|
|
|
|
idx = qla2x00_handle_to_idx(handle);
|
|
iodesc = &ha->io_descriptors[idx];
|
|
if (iodesc)
|
|
if (iodesc->signature != handle)
|
|
iodesc = NULL;
|
|
|
|
return (iodesc);
|
|
}
|
|
|
|
|
|
/**
|
|
* IO descriptor allocation routines.
|
|
**/
|
|
|
|
/**
|
|
* qla2x00_alloc_iodesc() - Allocate an IO descriptor from the pool.
|
|
* @ha: HA context
|
|
*
|
|
* Returns a pointer to the allocated io descriptor, or NULL, if none available.
|
|
*/
|
|
static inline struct io_descriptor *
|
|
qla2x00_alloc_iodesc(scsi_qla_host_t *ha)
|
|
{
|
|
uint16_t iter;
|
|
struct io_descriptor *iodesc;
|
|
|
|
iodesc = NULL;
|
|
for (iter = 0; iter < MAX_IO_DESCRIPTORS; iter++) {
|
|
if (ha->io_descriptors[iter].used)
|
|
continue;
|
|
|
|
iodesc = &ha->io_descriptors[iter];
|
|
iodesc->used = 1;
|
|
iodesc->idx = iter;
|
|
init_timer(&iodesc->timer);
|
|
iodesc->ha = ha;
|
|
iodesc->signature = qla2x00_iodesc_to_handle(iodesc);
|
|
break;
|
|
}
|
|
|
|
return (iodesc);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_free_iodesc() - Free an IO descriptor.
|
|
* @iodesc: io descriptor
|
|
*
|
|
* NOTE: The io descriptors timer *must* be stopped before it can be free'd.
|
|
*/
|
|
static inline void
|
|
qla2x00_free_iodesc(struct io_descriptor *iodesc)
|
|
{
|
|
iodesc->used = 0;
|
|
iodesc->signature = 0;
|
|
}
|
|
|
|
/**
|
|
* qla2x00_remove_iodesc_timer() - Remove an active timer from an IO descriptor.
|
|
* @iodesc: io descriptor
|
|
*/
|
|
static inline void
|
|
qla2x00_remove_iodesc_timer(struct io_descriptor *iodesc)
|
|
{
|
|
if (iodesc->timer.function != NULL) {
|
|
del_timer_sync(&iodesc->timer);
|
|
iodesc->timer.data = (unsigned long) NULL;
|
|
iodesc->timer.function = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* qla2x00_init_io_descriptors() - Initialize the pool of IO descriptors.
|
|
* @ha: HA context
|
|
*/
|
|
static inline void
|
|
qla2x00_init_io_descriptors(scsi_qla_host_t *ha)
|
|
{
|
|
uint16_t iter;
|
|
|
|
for (iter = 0; iter < MAX_IO_DESCRIPTORS; iter++) {
|
|
if (!ha->io_descriptors[iter].used)
|
|
continue;
|
|
|
|
qla2x00_remove_iodesc_timer(&ha->io_descriptors[iter]);
|
|
qla2x00_free_iodesc(&ha->io_descriptors[iter]);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* IO descriptor timer routines.
|
|
**/
|
|
|
|
/**
|
|
* qla2x00_iodesc_timeout() - Timeout IO descriptor handler.
|
|
* @data: io descriptor
|
|
*/
|
|
static void
|
|
qla2x00_iodesc_timeout(unsigned long data)
|
|
{
|
|
struct io_descriptor *iodesc;
|
|
|
|
iodesc = (struct io_descriptor *) data;
|
|
|
|
DEBUG14(printk("scsi(%ld): IO descriptor timeout, index=%x "
|
|
"signature=%08x, scheduling ISP abort.\n", iodesc->ha->host_no,
|
|
iodesc->idx, iodesc->signature));
|
|
|
|
qla2x00_free_iodesc(iodesc);
|
|
|
|
qla_printk(KERN_WARNING, iodesc->ha,
|
|
"IO descriptor timeout. Scheduling ISP abort.\n");
|
|
set_bit(ISP_ABORT_NEEDED, &iodesc->ha->dpc_flags);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_add_iodesc_timer() - Add and start a timer for an IO descriptor.
|
|
* @iodesc: io descriptor
|
|
*
|
|
* NOTE:
|
|
* The firmware shall timeout an outstanding mailbox IOCB in 2 * R_A_TOV (in
|
|
* tenths of a second) after it hits the wire. But, if there are any request
|
|
* resource contraints (i.e. during heavy I/O), exchanges can be held off for
|
|
* at most R_A_TOV. Therefore, the driver will wait 4 * R_A_TOV before
|
|
* scheduling a recovery (big hammer).
|
|
*/
|
|
static inline void
|
|
qla2x00_add_iodesc_timer(struct io_descriptor *iodesc)
|
|
{
|
|
unsigned long timeout;
|
|
|
|
timeout = (iodesc->ha->r_a_tov * 4) / 10;
|
|
init_timer(&iodesc->timer);
|
|
iodesc->timer.data = (unsigned long) iodesc;
|
|
iodesc->timer.expires = jiffies + (timeout * HZ);
|
|
iodesc->timer.function =
|
|
(void (*) (unsigned long)) qla2x00_iodesc_timeout;
|
|
add_timer(&iodesc->timer);
|
|
}
|
|
|
|
/**
|
|
* IO descriptor support routines.
|
|
**/
|
|
|
|
/**
|
|
* qla2x00_update_login_fcport() - Update fcport data after login processing.
|
|
* @ha: HA context
|
|
* @mbxstat: Mailbox command status IOCB
|
|
* @fcport: port to update
|
|
*/
|
|
static inline void
|
|
qla2x00_update_login_fcport(scsi_qla_host_t *ha, struct mbx_entry *mbxstat,
|
|
fc_port_t *fcport)
|
|
{
|
|
if (le16_to_cpu(mbxstat->mb1) & BIT_0) {
|
|
fcport->port_type = FCT_INITIATOR;
|
|
} else {
|
|
fcport->port_type = FCT_TARGET;
|
|
if (le16_to_cpu(mbxstat->mb1) & BIT_1) {
|
|
fcport->flags |= FCF_TAPE_PRESENT;
|
|
}
|
|
}
|
|
fcport->login_retry = 0;
|
|
fcport->port_login_retry_count = ha->port_down_retry_count *
|
|
PORT_RETRY_TIME;
|
|
atomic_set(&fcport->port_down_timer, ha->port_down_retry_count *
|
|
PORT_RETRY_TIME);
|
|
fcport->flags |= FCF_FABRIC_DEVICE;
|
|
fcport->flags &= ~FCF_FAILOVER_NEEDED;
|
|
fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
|
|
atomic_set(&fcport->state, FCS_ONLINE);
|
|
schedule_work(&fcport->rport_add_work);
|
|
}
|
|
|
|
|
|
/**
|
|
* Mailbox IOCB commands.
|
|
**/
|
|
|
|
/**
|
|
* qla2x00_get_mbx_iocb_entry() - Retrieve an IOCB from the request queue.
|
|
* @ha: HA context
|
|
* @handle: handle to io descriptor
|
|
*
|
|
* Returns a pointer to the reqest entry, or NULL, if none were available.
|
|
*/
|
|
static inline struct mbx_entry *
|
|
qla2x00_get_mbx_iocb_entry(scsi_qla_host_t *ha, uint32_t handle)
|
|
{
|
|
uint16_t cnt;
|
|
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
|
|
struct mbx_entry *mbxentry;
|
|
|
|
mbxentry = NULL;
|
|
|
|
if (ha->req_q_cnt < 3) {
|
|
cnt = qla2x00_debounce_register(ISP_REQ_Q_OUT(ha, reg));
|
|
if (ha->req_ring_index < cnt)
|
|
ha->req_q_cnt = cnt - ha->req_ring_index;
|
|
else
|
|
ha->req_q_cnt = ha->request_q_length -
|
|
(ha->req_ring_index - cnt);
|
|
}
|
|
if (ha->req_q_cnt >= 3) {
|
|
mbxentry = (struct mbx_entry *)ha->request_ring_ptr;
|
|
|
|
memset(mbxentry, 0, sizeof(struct mbx_entry));
|
|
mbxentry->entry_type = MBX_IOCB_TYPE;
|
|
mbxentry->entry_count = 1;
|
|
mbxentry->sys_define1 = SOURCE_ASYNC_IOCB;
|
|
mbxentry->handle = handle;
|
|
}
|
|
return (mbxentry);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_send_abort_iocb() - Issue an abort IOCB to the firmware.
|
|
* @ha: HA context
|
|
* @iodesc: io descriptor
|
|
* @handle_to_abort: firmware handle to abort
|
|
* @ha_locked: is function called with the hardware lock
|
|
*
|
|
* Returns QLA_SUCCESS if the IOCB was issued.
|
|
*/
|
|
static int
|
|
qla2x00_send_abort_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
|
|
uint32_t handle_to_abort, int ha_locked)
|
|
{
|
|
unsigned long flags = 0;
|
|
struct mbx_entry *mbxentry;
|
|
|
|
/* Send marker if required. */
|
|
if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
|
|
return (QLA_FUNCTION_FAILED);
|
|
|
|
if (!ha_locked)
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
/* Build abort mailbox IOCB. */
|
|
mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
|
|
if (mbxentry == NULL) {
|
|
if (!ha_locked)
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
return (QLA_FUNCTION_FAILED);
|
|
}
|
|
mbxentry->mb0 = __constant_cpu_to_le16(MBC_ABORT_COMMAND);
|
|
mbxentry->mb1 = mbxentry->loop_id.extended =
|
|
cpu_to_le16(iodesc->remote_fcport->loop_id);
|
|
mbxentry->mb2 = LSW(handle_to_abort);
|
|
mbxentry->mb3 = MSW(handle_to_abort);
|
|
wmb();
|
|
|
|
qla2x00_add_iodesc_timer(iodesc);
|
|
|
|
/* Issue command to ISP. */
|
|
qla2x00_isp_cmd(ha);
|
|
|
|
if (!ha_locked)
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
DEBUG14(printk("scsi(%ld): Sending Abort IOCB (%08x) to [%x], aborting "
|
|
"%08x.\n", ha->host_no, iodesc->signature,
|
|
iodesc->remote_fcport->loop_id, handle_to_abort));
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_send_abort_iocb_cb() - Abort IOCB callback.
|
|
* @ha: HA context
|
|
* @iodesc: io descriptor
|
|
* @mbxstat: mailbox status IOCB
|
|
*
|
|
* Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
|
|
* will be used for a retry.
|
|
*/
|
|
static int
|
|
qla2x00_send_abort_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
|
|
struct mbx_entry *mbxstat)
|
|
{
|
|
DEBUG14(printk("scsi(%ld): Abort IOCB -- sent to [%x/%02x%02x%02x], "
|
|
"status=%x mb0=%x.\n", ha->host_no, iodesc->remote_fcport->loop_id,
|
|
iodesc->d_id.b.domain, iodesc->d_id.b.area, iodesc->d_id.b.al_pa,
|
|
le16_to_cpu(mbxstat->status), le16_to_cpu(mbxstat->mb0)));
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
|
|
/**
|
|
* qla2x00_send_adisc_iocb() - Issue a Get Port Database IOCB to the firmware.
|
|
* @ha: HA context
|
|
* @iodesc: io descriptor
|
|
* @ha_locked: is function called with the hardware lock
|
|
*
|
|
* Returns QLA_SUCCESS if the IOCB was issued.
|
|
*/
|
|
static int
|
|
qla2x00_send_adisc_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
|
|
int ha_locked)
|
|
{
|
|
unsigned long flags = 0;
|
|
struct mbx_entry *mbxentry;
|
|
|
|
/* Send marker if required. */
|
|
if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
|
|
return (QLA_FUNCTION_FAILED);
|
|
|
|
if (!ha_locked)
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
/* Build Get Port Database IOCB. */
|
|
mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
|
|
if (mbxentry == NULL) {
|
|
if (!ha_locked)
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
return (QLA_FUNCTION_FAILED);
|
|
}
|
|
mbxentry->mb0 = __constant_cpu_to_le16(MBC_GET_PORT_DATABASE);
|
|
mbxentry->mb1 = mbxentry->loop_id.extended =
|
|
cpu_to_le16(iodesc->remote_fcport->loop_id);
|
|
mbxentry->mb2 = cpu_to_le16(MSW(LSD(ha->iodesc_pd_dma)));
|
|
mbxentry->mb3 = cpu_to_le16(LSW(LSD(ha->iodesc_pd_dma)));
|
|
mbxentry->mb6 = cpu_to_le16(MSW(MSD(ha->iodesc_pd_dma)));
|
|
mbxentry->mb7 = cpu_to_le16(LSW(MSD(ha->iodesc_pd_dma)));
|
|
mbxentry->mb10 = __constant_cpu_to_le16(BIT_0);
|
|
wmb();
|
|
|
|
qla2x00_add_iodesc_timer(iodesc);
|
|
|
|
/* Issue command to ISP. */
|
|
qla2x00_isp_cmd(ha);
|
|
|
|
if (!ha_locked)
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
DEBUG14(printk("scsi(%ld): Sending Adisc IOCB (%08x) to [%x].\n",
|
|
ha->host_no, iodesc->signature, iodesc->remote_fcport->loop_id));
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_send_adisc_iocb_cb() - Get Port Database IOCB callback.
|
|
* @ha: HA context
|
|
* @iodesc: io descriptor
|
|
* @mbxstat: mailbox status IOCB
|
|
*
|
|
* Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
|
|
* will be used for a retry.
|
|
*/
|
|
static int
|
|
qla2x00_send_adisc_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
|
|
struct mbx_entry *mbxstat)
|
|
{
|
|
fc_port_t *remote_fcport;
|
|
|
|
remote_fcport = iodesc->remote_fcport;
|
|
|
|
/* Ensure the port IDs are consistent. */
|
|
if (remote_fcport->d_id.b24 != iodesc->d_id.b24) {
|
|
DEBUG14(printk("scsi(%ld): Adisc IOCB -- ignoring, remote port "
|
|
"id changed from [%02x%02x%02x] to [%02x%02x%02x].\n",
|
|
ha->host_no, remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa,
|
|
iodesc->d_id.b.domain, iodesc->d_id.b.area,
|
|
iodesc->d_id.b.al_pa));
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/* Only process the last command. */
|
|
if (remote_fcport->iodesc_idx_sent != iodesc->idx) {
|
|
DEBUG14(printk("scsi(%ld): Adisc IOCB -- ignoring, sent to "
|
|
"[%02x%02x%02x], expected %x, received %x.\n", ha->host_no,
|
|
iodesc->d_id.b.domain, iodesc->d_id.b.area,
|
|
iodesc->d_id.b.al_pa, remote_fcport->iodesc_idx_sent,
|
|
iodesc->idx));
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
if (le16_to_cpu(mbxstat->status) == CS_COMPLETE) {
|
|
DEBUG14(printk("scsi(%ld): Adisc IOCB -- marking "
|
|
"[%x/%02x%02x%02x] online.\n", ha->host_no,
|
|
remote_fcport->loop_id, remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa));
|
|
|
|
atomic_set(&remote_fcport->state, FCS_ONLINE);
|
|
} else {
|
|
DEBUG14(printk("scsi(%ld): Adisc IOCB -- marking "
|
|
"[%x/%02x%02x%02x] lost, status=%x mb0=%x.\n", ha->host_no,
|
|
remote_fcport->loop_id, remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa,
|
|
le16_to_cpu(mbxstat->status), le16_to_cpu(mbxstat->mb0)));
|
|
|
|
if (atomic_read(&remote_fcport->state) != FCS_DEVICE_DEAD)
|
|
atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
|
|
}
|
|
remote_fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
|
|
/**
|
|
* qla2x00_send_logout_iocb() - Issue a fabric port logout IOCB to the firmware.
|
|
* @ha: HA context
|
|
* @iodesc: io descriptor
|
|
* @ha_locked: is function called with the hardware lock
|
|
*
|
|
* Returns QLA_SUCCESS if the IOCB was issued.
|
|
*/
|
|
static int
|
|
qla2x00_send_logout_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
|
|
int ha_locked)
|
|
{
|
|
unsigned long flags = 0;
|
|
struct mbx_entry *mbxentry;
|
|
|
|
/* Send marker if required. */
|
|
if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
|
|
return (QLA_FUNCTION_FAILED);
|
|
|
|
if (!ha_locked)
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
/* Build fabric port logout mailbox IOCB. */
|
|
mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
|
|
if (mbxentry == NULL) {
|
|
if (!ha_locked)
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
return (QLA_FUNCTION_FAILED);
|
|
}
|
|
mbxentry->mb0 = __constant_cpu_to_le16(MBC_LOGOUT_FABRIC_PORT);
|
|
mbxentry->mb1 = mbxentry->loop_id.extended =
|
|
cpu_to_le16(iodesc->remote_fcport->loop_id);
|
|
wmb();
|
|
|
|
qla2x00_add_iodesc_timer(iodesc);
|
|
|
|
/* Issue command to ISP. */
|
|
qla2x00_isp_cmd(ha);
|
|
|
|
if (!ha_locked)
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
DEBUG14(printk("scsi(%ld): Sending Logout IOCB (%08x) to [%x].\n",
|
|
ha->host_no, iodesc->signature, iodesc->remote_fcport->loop_id));
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_send_logout_iocb_cb() - Fabric port logout IOCB callback.
|
|
* @ha: HA context
|
|
* @iodesc: io descriptor
|
|
* @mbxstat: mailbox status IOCB
|
|
*
|
|
* Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
|
|
* will be used for a retry.
|
|
*/
|
|
static int
|
|
qla2x00_send_logout_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
|
|
struct mbx_entry *mbxstat)
|
|
{
|
|
DEBUG14(printk("scsi(%ld): Logout IOCB -- sent to [%x/%02x%02x%02x], "
|
|
"status=%x mb0=%x mb1=%x.\n", ha->host_no,
|
|
iodesc->remote_fcport->loop_id,
|
|
iodesc->remote_fcport->d_id.b.domain,
|
|
iodesc->remote_fcport->d_id.b.area,
|
|
iodesc->remote_fcport->d_id.b.al_pa, le16_to_cpu(mbxstat->status),
|
|
le16_to_cpu(mbxstat->mb0), le16_to_cpu(mbxstat->mb1)));
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
|
|
/**
|
|
* qla2x00_send_login_iocb() - Issue a fabric port login IOCB to the firmware.
|
|
* @ha: HA context
|
|
* @iodesc: io descriptor
|
|
* @d_id: port id for device
|
|
* @ha_locked: is function called with the hardware lock
|
|
*
|
|
* Returns QLA_SUCCESS if the IOCB was issued.
|
|
*/
|
|
static int
|
|
qla2x00_send_login_iocb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
|
|
port_id_t *d_id, int ha_locked)
|
|
{
|
|
unsigned long flags = 0;
|
|
struct mbx_entry *mbxentry;
|
|
|
|
/* Send marker if required. */
|
|
if (qla2x00_issue_marker(ha, ha_locked) != QLA_SUCCESS)
|
|
return (QLA_FUNCTION_FAILED);
|
|
|
|
if (!ha_locked)
|
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
|
|
|
/* Build fabric port login mailbox IOCB. */
|
|
mbxentry = qla2x00_get_mbx_iocb_entry(ha, iodesc->signature);
|
|
if (mbxentry == NULL) {
|
|
if (!ha_locked)
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
return (QLA_FUNCTION_FAILED);
|
|
}
|
|
mbxentry->mb0 = __constant_cpu_to_le16(MBC_LOGIN_FABRIC_PORT);
|
|
mbxentry->mb1 = mbxentry->loop_id.extended =
|
|
cpu_to_le16(iodesc->remote_fcport->loop_id);
|
|
mbxentry->mb2 = cpu_to_le16(d_id->b.domain);
|
|
mbxentry->mb3 = cpu_to_le16(d_id->b.area << 8 | d_id->b.al_pa);
|
|
mbxentry->mb10 = __constant_cpu_to_le16(BIT_0);
|
|
wmb();
|
|
|
|
qla2x00_add_iodesc_timer(iodesc);
|
|
|
|
/* Issue command to ISP. */
|
|
qla2x00_isp_cmd(ha);
|
|
|
|
if (!ha_locked)
|
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
|
|
|
DEBUG14(printk("scsi(%ld): Sending Login IOCB (%08x) to "
|
|
"[%x/%02x%02x%02x].\n", ha->host_no, iodesc->signature,
|
|
iodesc->remote_fcport->loop_id, d_id->b.domain, d_id->b.area,
|
|
d_id->b.al_pa));
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_send_login_iocb_cb() - Fabric port logout IOCB callback.
|
|
* @ha: HA context
|
|
* @iodesc: io descriptor
|
|
* @mbxstat: mailbox status IOCB
|
|
*
|
|
* Returns QLA_SUCCESS if @iodesc can be freed by the caller, else, @iodesc
|
|
* will be used for a retry.
|
|
*/
|
|
static int
|
|
qla2x00_send_login_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc,
|
|
struct mbx_entry *mbxstat)
|
|
{
|
|
int rval;
|
|
fc_port_t *fcport, *remote_fcport, *exist_fcport;
|
|
struct io_descriptor *abort_iodesc, *login_iodesc;
|
|
uint16_t status, mb[8];
|
|
uint16_t reuse;
|
|
uint16_t remote_loopid;
|
|
port_id_t remote_did, inuse_did;
|
|
|
|
remote_fcport = iodesc->remote_fcport;
|
|
|
|
/* Only process the last command. */
|
|
if (remote_fcport->iodesc_idx_sent != iodesc->idx) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- ignoring, sent to "
|
|
"[%02x%02x%02x], expected %x, received %x.\n",
|
|
ha->host_no, iodesc->d_id.b.domain, iodesc->d_id.b.area,
|
|
iodesc->d_id.b.al_pa, remote_fcport->iodesc_idx_sent,
|
|
iodesc->idx));
|
|
|
|
/* Free RSCN fcport resources. */
|
|
if (remote_fcport->port_type == FCT_RSCN) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- Freeing RSCN "
|
|
"fcport %p [%x/%02x%02x%02x] given ignored Login "
|
|
"IOCB.\n", ha->host_no, remote_fcport,
|
|
remote_fcport->loop_id,
|
|
remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area,
|
|
remote_fcport->d_id.b.al_pa));
|
|
|
|
list_del(&remote_fcport->list);
|
|
kfree(remote_fcport);
|
|
}
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
status = le16_to_cpu(mbxstat->status);
|
|
mb[0] = le16_to_cpu(mbxstat->mb0);
|
|
mb[1] = le16_to_cpu(mbxstat->mb1);
|
|
mb[2] = le16_to_cpu(mbxstat->mb2);
|
|
mb[6] = le16_to_cpu(mbxstat->mb6);
|
|
mb[7] = le16_to_cpu(mbxstat->mb7);
|
|
|
|
/* Good status? */
|
|
if ((status == CS_COMPLETE || status == CS_COMPLETE_CHKCOND) &&
|
|
mb[0] == MBS_COMMAND_COMPLETE) {
|
|
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- status=%x mb1=%x pn="
|
|
"%02x%02x%02x%02x%02x%02x%02x%02x.\n", ha->host_no, status,
|
|
mb[1], mbxstat->port_name[0], mbxstat->port_name[1],
|
|
mbxstat->port_name[2], mbxstat->port_name[3],
|
|
mbxstat->port_name[4], mbxstat->port_name[5],
|
|
mbxstat->port_name[6], mbxstat->port_name[7]));
|
|
|
|
memcpy(remote_fcport->node_name, mbxstat->node_name, WWN_SIZE);
|
|
memcpy(remote_fcport->port_name, mbxstat->port_name, WWN_SIZE);
|
|
|
|
/* Is the device already in our fcports list? */
|
|
if (remote_fcport->port_type != FCT_RSCN) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- marking "
|
|
"[%x/%02x%02x%02x] online.\n", ha->host_no,
|
|
remote_fcport->loop_id,
|
|
remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area,
|
|
remote_fcport->d_id.b.al_pa));
|
|
|
|
qla2x00_update_login_fcport(ha, mbxstat, remote_fcport);
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/* Does the RSCN portname already exist in our fcports list? */
|
|
exist_fcport = NULL;
|
|
list_for_each_entry(fcport, &ha->fcports, list) {
|
|
if (memcmp(remote_fcport->port_name, fcport->port_name,
|
|
WWN_SIZE) == 0) {
|
|
exist_fcport = fcport;
|
|
break;
|
|
}
|
|
}
|
|
if (exist_fcport != NULL) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- found RSCN "
|
|
"fcport in fcports list [%p].\n", ha->host_no,
|
|
exist_fcport));
|
|
|
|
/* Abort any ADISC that could have been sent. */
|
|
if (exist_fcport->iodesc_idx_sent != iodesc->idx &&
|
|
exist_fcport->iodesc_idx_sent <
|
|
MAX_IO_DESCRIPTORS &&
|
|
ha->io_descriptors[exist_fcport->iodesc_idx_sent].
|
|
cb_idx == ADISC_PORT_IOCB_CB) {
|
|
|
|
abort_iodesc = qla2x00_alloc_iodesc(ha);
|
|
if (abort_iodesc) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB "
|
|
"-- issuing abort to outstanding "
|
|
"Adisc [%x/%02x%02x%02x].\n",
|
|
ha->host_no, remote_fcport->loop_id,
|
|
exist_fcport->d_id.b.domain,
|
|
exist_fcport->d_id.b.area,
|
|
exist_fcport->d_id.b.al_pa));
|
|
|
|
abort_iodesc->cb_idx = ABORT_IOCB_CB;
|
|
abort_iodesc->d_id.b24 =
|
|
exist_fcport->d_id.b24;
|
|
abort_iodesc->remote_fcport =
|
|
exist_fcport;
|
|
exist_fcport->iodesc_idx_sent =
|
|
abort_iodesc->idx;
|
|
qla2x00_send_abort_iocb(ha,
|
|
abort_iodesc, ha->io_descriptors[
|
|
exist_fcport->iodesc_idx_sent].
|
|
signature, 1);
|
|
} else {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB "
|
|
"-- unable to abort outstanding "
|
|
"Adisc [%x/%02x%02x%02x].\n",
|
|
ha->host_no, remote_fcport->loop_id,
|
|
exist_fcport->d_id.b.domain,
|
|
exist_fcport->d_id.b.area,
|
|
exist_fcport->d_id.b.al_pa));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the existing fcport is waiting to send an ADISC
|
|
* or LOGIN, then reuse remote fcport (RSCN) to
|
|
* continue waiting.
|
|
*/
|
|
reuse = 0;
|
|
remote_loopid = remote_fcport->loop_id;
|
|
remote_did.b24 = remote_fcport->d_id.b24;
|
|
if (exist_fcport->iodesc_idx_sent ==
|
|
IODESC_ADISC_NEEDED ||
|
|
exist_fcport->iodesc_idx_sent ==
|
|
IODESC_LOGIN_NEEDED) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- "
|
|
"existing fcport [%x/%02x%02x%02x] "
|
|
"waiting for IO descriptor, reuse RSCN "
|
|
"fcport.\n", ha->host_no,
|
|
exist_fcport->loop_id,
|
|
exist_fcport->d_id.b.domain,
|
|
exist_fcport->d_id.b.area,
|
|
exist_fcport->d_id.b.al_pa));
|
|
|
|
reuse++;
|
|
remote_fcport->iodesc_idx_sent =
|
|
exist_fcport->iodesc_idx_sent;
|
|
exist_fcport->iodesc_idx_sent =
|
|
IODESC_INVALID_INDEX;
|
|
remote_fcport->loop_id = exist_fcport->loop_id;
|
|
remote_fcport->d_id.b24 =
|
|
exist_fcport->d_id.b24;
|
|
}
|
|
|
|
/* Logout the old loopid. */
|
|
if (!reuse &&
|
|
exist_fcport->loop_id != remote_fcport->loop_id &&
|
|
exist_fcport->loop_id != FC_NO_LOOP_ID) {
|
|
login_iodesc = qla2x00_alloc_iodesc(ha);
|
|
if (login_iodesc) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB "
|
|
"-- issuing logout to free old "
|
|
"loop id [%x/%02x%02x%02x].\n",
|
|
ha->host_no, exist_fcport->loop_id,
|
|
exist_fcport->d_id.b.domain,
|
|
exist_fcport->d_id.b.area,
|
|
exist_fcport->d_id.b.al_pa));
|
|
|
|
login_iodesc->cb_idx =
|
|
LOGOUT_PORT_IOCB_CB;
|
|
login_iodesc->d_id.b24 =
|
|
exist_fcport->d_id.b24;
|
|
login_iodesc->remote_fcport =
|
|
exist_fcport;
|
|
exist_fcport->iodesc_idx_sent =
|
|
login_iodesc->idx;
|
|
qla2x00_send_logout_iocb(ha,
|
|
login_iodesc, 1);
|
|
} else {
|
|
/* Ran out of IO descriptiors. */
|
|
DEBUG14(printk("scsi(%ld): Login IOCB "
|
|
"-- unable to logout to free old "
|
|
"loop id [%x/%02x%02x%02x].\n",
|
|
ha->host_no, exist_fcport->loop_id,
|
|
exist_fcport->d_id.b.domain,
|
|
exist_fcport->d_id.b.area,
|
|
exist_fcport->d_id.b.al_pa));
|
|
|
|
exist_fcport->iodesc_idx_sent =
|
|
IODESC_INVALID_INDEX;
|
|
}
|
|
|
|
}
|
|
|
|
/* Update existing fcport with remote fcport info. */
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- marking "
|
|
"existing fcport [%x/%02x%02x%02x] online.\n",
|
|
ha->host_no, remote_loopid, remote_did.b.domain,
|
|
remote_did.b.area, remote_did.b.al_pa));
|
|
|
|
memcpy(exist_fcport->node_name,
|
|
remote_fcport->node_name, WWN_SIZE);
|
|
exist_fcport->loop_id = remote_loopid;
|
|
exist_fcport->d_id.b24 = remote_did.b24;
|
|
qla2x00_update_login_fcport(ha, mbxstat, exist_fcport);
|
|
|
|
/* Finally, free the remote (RSCN) fcport. */
|
|
if (!reuse) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- "
|
|
"Freeing RSCN fcport %p "
|
|
"[%x/%02x%02x%02x].\n", ha->host_no,
|
|
remote_fcport, remote_fcport->loop_id,
|
|
remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area,
|
|
remote_fcport->d_id.b.al_pa));
|
|
|
|
list_del(&remote_fcport->list);
|
|
kfree(remote_fcport);
|
|
}
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* A new device has been added, move the RSCN fcport to our
|
|
* fcports list.
|
|
*/
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- adding RSCN fcport "
|
|
"[%x/%02x%02x%02x] to fcports list.\n", ha->host_no,
|
|
remote_fcport->loop_id, remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area, remote_fcport->d_id.b.al_pa));
|
|
|
|
list_del(&remote_fcport->list);
|
|
remote_fcport->flags = (FCF_RLC_SUPPORT | FCF_RESCAN_NEEDED);
|
|
qla2x00_update_login_fcport(ha, mbxstat, remote_fcport);
|
|
list_add_tail(&remote_fcport->list, &ha->fcports);
|
|
set_bit(FCPORT_RESCAN_NEEDED, &ha->dpc_flags);
|
|
} else {
|
|
/* Handle login failure. */
|
|
if (remote_fcport->login_retry != 0) {
|
|
if (mb[0] == MBS_LOOP_ID_USED) {
|
|
inuse_did.b.domain = LSB(mb[1]);
|
|
inuse_did.b.area = MSB(mb[2]);
|
|
inuse_did.b.al_pa = LSB(mb[2]);
|
|
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- loop "
|
|
"id [%x] used by port id [%02x%02x%02x].\n",
|
|
ha->host_no, remote_fcport->loop_id,
|
|
inuse_did.b.domain, inuse_did.b.area,
|
|
inuse_did.b.al_pa));
|
|
|
|
if (remote_fcport->d_id.b24 ==
|
|
INVALID_PORT_ID) {
|
|
/*
|
|
* Invalid port id means we are trying
|
|
* to login to a remote port with just
|
|
* a loop id without knowing about the
|
|
* port id. Copy the port id and try
|
|
* again.
|
|
*/
|
|
remote_fcport->d_id.b24 = inuse_did.b24;
|
|
iodesc->d_id.b24 = inuse_did.b24;
|
|
} else {
|
|
remote_fcport->loop_id++;
|
|
rval = qla2x00_find_new_loop_id(ha,
|
|
remote_fcport);
|
|
if (rval == QLA_FUNCTION_FAILED) {
|
|
/* No more loop ids. */
|
|
return (QLA_SUCCESS);
|
|
}
|
|
}
|
|
} else if (mb[0] == MBS_PORT_ID_USED) {
|
|
/*
|
|
* Device has another loop ID. The firmware
|
|
* group recommends the driver perform an
|
|
* implicit login with the specified ID.
|
|
*/
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- port "
|
|
"id [%02x%02x%02x] already assigned to "
|
|
"loop id [%x].\n", ha->host_no,
|
|
iodesc->d_id.b.domain, iodesc->d_id.b.area,
|
|
iodesc->d_id.b.al_pa, mb[1]));
|
|
|
|
remote_fcport->loop_id = mb[1];
|
|
|
|
} else {
|
|
/* Unable to perform login, try again. */
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- "
|
|
"failed login [%x/%02x%02x%02x], status=%x "
|
|
"mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n",
|
|
ha->host_no, remote_fcport->loop_id,
|
|
iodesc->d_id.b.domain, iodesc->d_id.b.area,
|
|
iodesc->d_id.b.al_pa, status, mb[0], mb[1],
|
|
mb[2], mb[6], mb[7]));
|
|
}
|
|
|
|
/* Reissue Login with the same IO descriptor. */
|
|
iodesc->signature =
|
|
qla2x00_iodesc_to_handle(iodesc);
|
|
iodesc->cb_idx = LOGIN_PORT_IOCB_CB;
|
|
iodesc->d_id.b24 = remote_fcport->d_id.b24;
|
|
remote_fcport->iodesc_idx_sent = iodesc->idx;
|
|
remote_fcport->login_retry--;
|
|
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- retrying "
|
|
"login to [%x/%02x%02x%02x] (%d).\n", ha->host_no,
|
|
remote_fcport->loop_id,
|
|
remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area,
|
|
remote_fcport->d_id.b.al_pa,
|
|
remote_fcport->login_retry));
|
|
|
|
qla2x00_send_login_iocb(ha, iodesc,
|
|
&remote_fcport->d_id, 1);
|
|
|
|
return (QLA_FUNCTION_FAILED);
|
|
} else {
|
|
/* No more logins, mark device dead. */
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- failed "
|
|
"login [%x/%02x%02x%02x] after retries, status=%x "
|
|
"mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n",
|
|
ha->host_no, remote_fcport->loop_id,
|
|
iodesc->d_id.b.domain, iodesc->d_id.b.area,
|
|
iodesc->d_id.b.al_pa, status, mb[0], mb[1],
|
|
mb[2], mb[6], mb[7]));
|
|
|
|
atomic_set(&remote_fcport->state, FCS_DEVICE_DEAD);
|
|
if (remote_fcport->port_type == FCT_RSCN) {
|
|
DEBUG14(printk("scsi(%ld): Login IOCB -- "
|
|
"Freeing dead RSCN fcport %p "
|
|
"[%x/%02x%02x%02x].\n", ha->host_no,
|
|
remote_fcport, remote_fcport->loop_id,
|
|
remote_fcport->d_id.b.domain,
|
|
remote_fcport->d_id.b.area,
|
|
remote_fcport->d_id.b.al_pa));
|
|
|
|
list_del(&remote_fcport->list);
|
|
kfree(remote_fcport);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
|
|
/**
|
|
* IO descriptor processing routines.
|
|
**/
|
|
|
|
/**
|
|
* qla2x00_alloc_rscn_fcport() - Allocate an RSCN type fcport.
|
|
* @ha: HA context
|
|
* @flags: allocation flags
|
|
*
|
|
* Returns a pointer to the allocated RSCN fcport, or NULL, if none available.
|
|
*/
|
|
fc_port_t *
|
|
qla2x00_alloc_rscn_fcport(scsi_qla_host_t *ha, gfp_t flags)
|
|
{
|
|
fc_port_t *fcport;
|
|
|
|
fcport = qla2x00_alloc_fcport(ha, flags);
|
|
if (fcport == NULL)
|
|
return (fcport);
|
|
|
|
/* Setup RSCN fcport structure. */
|
|
fcport->port_type = FCT_RSCN;
|
|
|
|
return (fcport);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_handle_port_rscn() - Handle port RSCN.
|
|
* @ha: HA context
|
|
* @rscn_entry: RSCN entry
|
|
* @fcport: fcport entry to updated
|
|
*
|
|
* Returns QLA_SUCCESS if the port RSCN was handled.
|
|
*/
|
|
int
|
|
qla2x00_handle_port_rscn(scsi_qla_host_t *ha, uint32_t rscn_entry,
|
|
fc_port_t *known_fcport, int ha_locked)
|
|
{
|
|
int rval;
|
|
port_id_t rscn_pid;
|
|
fc_port_t *fcport, *remote_fcport, *rscn_fcport;
|
|
struct io_descriptor *iodesc;
|
|
|
|
remote_fcport = NULL;
|
|
rscn_fcport = NULL;
|
|
|
|
/* Prepare port id based on incoming entries. */
|
|
if (known_fcport) {
|
|
rscn_pid.b24 = known_fcport->d_id.b24;
|
|
remote_fcport = known_fcport;
|
|
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- process RSCN for "
|
|
"fcport [%02x%02x%02x].\n", ha->host_no,
|
|
remote_fcport->d_id.b.domain, remote_fcport->d_id.b.area,
|
|
remote_fcport->d_id.b.al_pa));
|
|
} else {
|
|
rscn_pid.b.domain = LSB(MSW(rscn_entry));
|
|
rscn_pid.b.area = MSB(LSW(rscn_entry));
|
|
rscn_pid.b.al_pa = LSB(LSW(rscn_entry));
|
|
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- process RSCN for "
|
|
"port id [%02x%02x%02x].\n", ha->host_no,
|
|
rscn_pid.b.domain, rscn_pid.b.area, rscn_pid.b.al_pa));
|
|
|
|
/*
|
|
* Search fcport lists for a known entry at the specified port
|
|
* ID.
|
|
*/
|
|
list_for_each_entry(fcport, &ha->fcports, list) {
|
|
if (rscn_pid.b24 == fcport->d_id.b24) {
|
|
remote_fcport = fcport;
|
|
break;
|
|
}
|
|
}
|
|
list_for_each_entry(fcport, &ha->rscn_fcports, list) {
|
|
if (rscn_pid.b24 == fcport->d_id.b24) {
|
|
rscn_fcport = fcport;
|
|
break;
|
|
}
|
|
}
|
|
if (remote_fcport == NULL)
|
|
remote_fcport = rscn_fcport;
|
|
}
|
|
|
|
/*
|
|
* If the port is already in our fcport list and online, send an ADISC
|
|
* to see if it's still alive. Issue login if a new fcport or the known
|
|
* fcport is currently offline.
|
|
*/
|
|
if (remote_fcport) {
|
|
/*
|
|
* No need to send request if the remote fcport is currently
|
|
* waiting for an available io descriptor.
|
|
*/
|
|
if (known_fcport == NULL &&
|
|
(remote_fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
|
|
remote_fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED)) {
|
|
/*
|
|
* If previous waiting io descriptor is an ADISC, then
|
|
* the new RSCN may come from a new remote fcport being
|
|
* plugged into the same location.
|
|
*/
|
|
if (remote_fcport->port_type == FCT_RSCN) {
|
|
remote_fcport->iodesc_idx_sent =
|
|
IODESC_LOGIN_NEEDED;
|
|
} else if (remote_fcport->iodesc_idx_sent ==
|
|
IODESC_ADISC_NEEDED) {
|
|
fc_port_t *new_fcport;
|
|
|
|
remote_fcport->iodesc_idx_sent =
|
|
IODESC_INVALID_INDEX;
|
|
|
|
/* Create new fcport for later login. */
|
|
new_fcport = qla2x00_alloc_rscn_fcport(ha,
|
|
ha_locked ? GFP_ATOMIC: GFP_KERNEL);
|
|
if (new_fcport) {
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN "
|
|
"-- creating RSCN fcport %p for "
|
|
"future login.\n", ha->host_no,
|
|
new_fcport));
|
|
|
|
new_fcport->d_id.b24 =
|
|
remote_fcport->d_id.b24;
|
|
new_fcport->iodesc_idx_sent =
|
|
IODESC_LOGIN_NEEDED;
|
|
|
|
list_add_tail(&new_fcport->list,
|
|
&ha->rscn_fcports);
|
|
set_bit(IODESC_PROCESS_NEEDED,
|
|
&ha->dpc_flags);
|
|
} else {
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN "
|
|
"-- unable to allocate RSCN fcport "
|
|
"for future login.\n",
|
|
ha->host_no));
|
|
}
|
|
}
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/* Send ADISC if the fcport is online */
|
|
if (atomic_read(&remote_fcport->state) == FCS_ONLINE ||
|
|
remote_fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED) {
|
|
|
|
atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
|
|
|
|
iodesc = qla2x00_alloc_iodesc(ha);
|
|
if (iodesc == NULL) {
|
|
/* Mark fcport for later adisc processing */
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- not "
|
|
"enough IO descriptors for Adisc, flag "
|
|
"for later processing.\n", ha->host_no));
|
|
|
|
remote_fcport->iodesc_idx_sent =
|
|
IODESC_ADISC_NEEDED;
|
|
set_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
iodesc->cb_idx = ADISC_PORT_IOCB_CB;
|
|
iodesc->d_id.b24 = rscn_pid.b24;
|
|
iodesc->remote_fcport = remote_fcport;
|
|
remote_fcport->iodesc_idx_sent = iodesc->idx;
|
|
qla2x00_send_adisc_iocb(ha, iodesc, ha_locked);
|
|
|
|
return (QLA_SUCCESS);
|
|
} else if (remote_fcport->iodesc_idx_sent <
|
|
MAX_IO_DESCRIPTORS &&
|
|
ha->io_descriptors[remote_fcport->iodesc_idx_sent].cb_idx ==
|
|
ADISC_PORT_IOCB_CB) {
|
|
/*
|
|
* Receiving another RSCN while an ADISC is pending,
|
|
* abort the IOCB. Use the same descriptor for the
|
|
* abort.
|
|
*/
|
|
uint32_t handle_to_abort;
|
|
|
|
iodesc = &ha->io_descriptors[
|
|
remote_fcport->iodesc_idx_sent];
|
|
qla2x00_remove_iodesc_timer(iodesc);
|
|
handle_to_abort = iodesc->signature;
|
|
iodesc->signature = qla2x00_iodesc_to_handle(iodesc);
|
|
iodesc->cb_idx = ABORT_IOCB_CB;
|
|
iodesc->d_id.b24 = remote_fcport->d_id.b24;
|
|
iodesc->remote_fcport = remote_fcport;
|
|
remote_fcport->iodesc_idx_sent = iodesc->idx;
|
|
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- issuing "
|
|
"abort to outstanding Adisc [%x/%02x%02x%02x].\n",
|
|
ha->host_no, remote_fcport->loop_id,
|
|
iodesc->d_id.b.domain, iodesc->d_id.b.area,
|
|
iodesc->d_id.b.al_pa));
|
|
|
|
qla2x00_send_abort_iocb(ha, iodesc, handle_to_abort,
|
|
ha_locked);
|
|
}
|
|
}
|
|
|
|
/* We need to login to the remote port, find it. */
|
|
if (known_fcport) {
|
|
remote_fcport = known_fcport;
|
|
} else if (rscn_fcport && rscn_fcport->d_id.b24 != INVALID_PORT_ID &&
|
|
rscn_fcport->iodesc_idx_sent < MAX_IO_DESCRIPTORS &&
|
|
ha->io_descriptors[rscn_fcport->iodesc_idx_sent].cb_idx ==
|
|
LOGIN_PORT_IOCB_CB) {
|
|
/*
|
|
* Ignore duplicate RSCN on fcport which has already
|
|
* initiated a login IOCB.
|
|
*/
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- ignoring, login "
|
|
"already sent to [%02x%02x%02x].\n", ha->host_no,
|
|
rscn_fcport->d_id.b.domain, rscn_fcport->d_id.b.area,
|
|
rscn_fcport->d_id.b.al_pa));
|
|
|
|
return (QLA_SUCCESS);
|
|
} else if (rscn_fcport && rscn_fcport->d_id.b24 != INVALID_PORT_ID &&
|
|
rscn_fcport != remote_fcport) {
|
|
/* Reuse same rscn fcport. */
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- reusing RSCN fcport "
|
|
"[%02x%02x%02x].\n", ha->host_no,
|
|
rscn_fcport->d_id.b.domain, rscn_fcport->d_id.b.area,
|
|
rscn_fcport->d_id.b.al_pa));
|
|
|
|
remote_fcport = rscn_fcport;
|
|
} else {
|
|
/* Create new fcport for later login. */
|
|
remote_fcport = qla2x00_alloc_rscn_fcport(ha,
|
|
ha_locked ? GFP_ATOMIC: GFP_KERNEL);
|
|
list_add_tail(&remote_fcport->list, &ha->rscn_fcports);
|
|
}
|
|
if (remote_fcport == NULL)
|
|
return (QLA_SUCCESS);
|
|
|
|
/* Prepare fcport for login. */
|
|
atomic_set(&remote_fcport->state, FCS_DEVICE_LOST);
|
|
remote_fcport->login_retry = 3; /* ha->login_retry_count; */
|
|
remote_fcport->d_id.b24 = rscn_pid.b24;
|
|
|
|
iodesc = qla2x00_alloc_iodesc(ha);
|
|
if (iodesc == NULL) {
|
|
/* Mark fcport for later adisc processing. */
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- not enough IO "
|
|
"descriptors for Login, flag for later processing.\n",
|
|
ha->host_no));
|
|
|
|
remote_fcport->iodesc_idx_sent = IODESC_LOGIN_NEEDED;
|
|
set_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
if (known_fcport == NULL || rscn_pid.b24 != INVALID_PORT_ID) {
|
|
remote_fcport->loop_id = ha->min_external_loopid;
|
|
|
|
rval = qla2x00_find_new_loop_id(ha, remote_fcport);
|
|
if (rval == QLA_FUNCTION_FAILED) {
|
|
/* No more loop ids, failed. */
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- no available "
|
|
"loop id to perform Login, failed.\n",
|
|
ha->host_no));
|
|
|
|
return (rval);
|
|
}
|
|
}
|
|
|
|
iodesc->cb_idx = LOGIN_PORT_IOCB_CB;
|
|
iodesc->d_id.b24 = rscn_pid.b24;
|
|
iodesc->remote_fcport = remote_fcport;
|
|
remote_fcport->iodesc_idx_sent = iodesc->idx;
|
|
|
|
DEBUG14(printk("scsi(%ld): Handle RSCN -- attempting login to "
|
|
"[%x/%02x%02x%02x].\n", ha->host_no, remote_fcport->loop_id,
|
|
iodesc->d_id.b.domain, iodesc->d_id.b.area, iodesc->d_id.b.al_pa));
|
|
|
|
qla2x00_send_login_iocb(ha, iodesc, &rscn_pid, ha_locked);
|
|
|
|
return (QLA_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_process_iodesc() - Complete IO descriptor processing.
|
|
* @ha: HA context
|
|
* @mbxstat: Mailbox IOCB status
|
|
*/
|
|
void
|
|
qla2x00_process_iodesc(scsi_qla_host_t *ha, struct mbx_entry *mbxstat)
|
|
{
|
|
int rval;
|
|
uint32_t signature;
|
|
fc_port_t *fcport;
|
|
struct io_descriptor *iodesc;
|
|
|
|
signature = mbxstat->handle;
|
|
|
|
DEBUG14(printk("scsi(%ld): Process IODesc -- processing %08x.\n",
|
|
ha->host_no, signature));
|
|
|
|
/* Retrieve proper IO descriptor. */
|
|
iodesc = qla2x00_handle_to_iodesc(ha, signature);
|
|
if (iodesc == NULL) {
|
|
DEBUG14(printk("scsi(%ld): Process IODesc -- ignoring, "
|
|
"incorrect signature %08x.\n", ha->host_no, signature));
|
|
|
|
return;
|
|
}
|
|
|
|
/* Stop IO descriptor timer. */
|
|
qla2x00_remove_iodesc_timer(iodesc);
|
|
|
|
/* Verify signature match. */
|
|
if (iodesc->signature != signature) {
|
|
DEBUG14(printk("scsi(%ld): Process IODesc -- ignoring, "
|
|
"signature mismatch, sent %08x, received %08x.\n",
|
|
ha->host_no, iodesc->signature, signature));
|
|
|
|
return;
|
|
}
|
|
|
|
/* Go with IOCB callback. */
|
|
rval = iocb_function_cb_list[iodesc->cb_idx](ha, iodesc, mbxstat);
|
|
if (rval != QLA_SUCCESS) {
|
|
/* IO descriptor reused by callback. */
|
|
return;
|
|
}
|
|
|
|
qla2x00_free_iodesc(iodesc);
|
|
|
|
if (test_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags)) {
|
|
/* Scan our fcports list for any RSCN requests. */
|
|
list_for_each_entry(fcport, &ha->fcports, list) {
|
|
if (fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
|
|
fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED) {
|
|
qla2x00_handle_port_rscn(ha, 0, fcport, 1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Scan our RSCN fcports list for any RSCN requests. */
|
|
list_for_each_entry(fcport, &ha->rscn_fcports, list) {
|
|
if (fcport->iodesc_idx_sent == IODESC_ADISC_NEEDED ||
|
|
fcport->iodesc_idx_sent == IODESC_LOGIN_NEEDED) {
|
|
qla2x00_handle_port_rscn(ha, 0, fcport, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
clear_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
|
|
}
|
|
|
|
/**
|
|
* qla2x00_cancel_io_descriptors() - Cancel all outstanding io descriptors.
|
|
* @ha: HA context
|
|
*
|
|
* This routine will also delete any RSCN entries related to the outstanding
|
|
* IO descriptors.
|
|
*/
|
|
void
|
|
qla2x00_cancel_io_descriptors(scsi_qla_host_t *ha)
|
|
{
|
|
fc_port_t *fcport, *fcptemp;
|
|
|
|
clear_bit(IODESC_PROCESS_NEEDED, &ha->dpc_flags);
|
|
|
|
/* Abort all IO descriptors. */
|
|
qla2x00_init_io_descriptors(ha);
|
|
|
|
/* Reset all pending IO descriptors in fcports list. */
|
|
list_for_each_entry(fcport, &ha->fcports, list) {
|
|
fcport->iodesc_idx_sent = IODESC_INVALID_INDEX;
|
|
}
|
|
|
|
/* Reset all pending IO descriptors in rscn fcports list. */
|
|
list_for_each_entry_safe(fcport, fcptemp, &ha->rscn_fcports, list) {
|
|
DEBUG14(printk("scsi(%ld): Cancel IOs -- Freeing RSCN fcport "
|
|
"%p [%x/%02x%02x%02x].\n", ha->host_no, fcport,
|
|
fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
|
|
fcport->d_id.b.al_pa));
|
|
|
|
list_del(&fcport->list);
|
|
kfree(fcport);
|
|
}
|
|
}
|