/* * QLogic Fibre Channel HBA Driver * Copyright (c) 2003-2005 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" #include /** * 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); if (fcport->rport) fc_remote_port_unblock(fcport->rport); } /** * 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); } }