| /* |
| * 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); |
| } |
| } |