diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c index e03256183ac6..30d9bcd3795f 100644 --- a/drivers/scsi/fnic/fdls_disc.c +++ b/drivers/scsi/fnic/fdls_disc.c @@ -327,12 +327,19 @@ void fdls_schedule_oxid_free_retry_work(struct work_struct *work) return; } - clear_bit(idx, oxid_pool->pending_schedule_free); reclaim_entry->oxid_idx = idx; reclaim_entry->expires = round_jiffies(jiffies + delay_j); + spin_lock_irqsave(&fnic->fnic_lock, flags); + /* Reset may have cleared this bit while this worker allocated. */ + if (!test_and_clear_bit(idx, oxid_pool->pending_schedule_free)) { + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + kfree(reclaim_entry); + continue; + } list_add_tail(&reclaim_entry->links, &oxid_pool->oxid_reclaim_list); spin_unlock_irqrestore(&fnic->fnic_lock, flags); + schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j); } } @@ -387,10 +394,25 @@ static bool fdls_is_oxid_tgt_req(uint16_t oxid) return true; } +static inline bool fdls_is_oxid_nvme_req(uint16_t oxid) +{ + return FNIC_FRAME_TYPE(oxid) == FNIC_FRAME_TYPE_NVME_LS; +} + static void fdls_reset_oxid_pool(struct fnic_iport_s *iport) { struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool; + struct reclaim_entry_s *reclaim_entry, *next; + cancel_delayed_work(&oxid_pool->oxid_reclaim_work); + cancel_delayed_work(&oxid_pool->schedule_oxid_free_retry); + list_for_each_entry_safe(reclaim_entry, next, + &oxid_pool->oxid_reclaim_list, links) { + list_del(&reclaim_entry->links); + kfree(reclaim_entry); + } + bitmap_clear(oxid_pool->pending_schedule_free, 0, FNIC_OXID_POOL_SZ); + bitmap_clear(oxid_pool->bitmap, 0, FNIC_OXID_POOL_SZ); oxid_pool->next_idx = 0; } @@ -1288,6 +1310,10 @@ bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport) spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); fnic_rport_exch_reset(iport->fnic, tport->fcid); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + } else if (IS_FNIC_NVME_INITIATOR(fnic)) { + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + nvfnic_exch_reset(iport, tport); + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); } if ((tport->flags & FNIC_FDLS_SCSI_REGISTERED) || @@ -1829,6 +1855,7 @@ static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport, tport->fcid = fcid; tport->wwpn = wwpn; tport->iport = iport; + INIT_LIST_HEAD(&tport->ls_req_list); FNIC_FCS_DBG(KERN_DEBUG, fnic, "Need to setup tport timer callback"); @@ -2440,6 +2467,8 @@ static void fdls_tport_timer_callback(struct timer_list *t) struct fnic *fnic = iport->fnic; uint16_t oxid; unsigned long flags; + struct fc_frame_header fchdr = {0}; + uint8_t fcid[3]; spin_lock_irqsave(&fnic->fnic_lock, flags); if (!tport->timer_pending) { @@ -2532,6 +2561,12 @@ static void fdls_tport_timer_callback(struct timer_list *t) FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x timeout tport 0x%x oxid 0x%x state %d\n", iport->fcid, tport->fcid, oxid, tport->state); + if (IS_FNIC_NVME_INITIATOR(fnic)) { + hton24(fcid, tport->fcid); + FNIC_STD_SET_S_ID(fchdr, fcid); + FNIC_STD_SET_OX_ID(fchdr, oxid); + nvfnic_process_ls_abts_rsp(iport, &fchdr); + } break; } spin_unlock_irqrestore(&fnic->fnic_lock, flags); @@ -2842,6 +2877,12 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, "mismatched target zoned with FC SCSI initiator: 0x%x", tgt_fcid); mismatched_tgt = true; + } else if (IS_FNIC_NVME_INITIATOR(fnic) && + prli_rsp->sp.spp_type != FC_TYPE_NVME) { + FNIC_FCS_DBG(KERN_ERR, fnic, + "mismatched target zoned with NVME initiator: 0x%x", + tgt_fcid); + mismatched_tgt = true; } if (mismatched_tgt) { fdls_tgt_logout(iport, tport); @@ -4853,6 +4894,8 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, return FNIC_FDMI_BLS_ABTS_RSP; } else if (fdls_is_oxid_tgt_req(oxid)) { return FNIC_TPORT_BLS_ABTS_RSP; + } else if (fdls_is_oxid_nvme_req(oxid)) { + return FNIC_LS_REQ_ABTS_RSP; } FNIC_FCS_DBG(KERN_INFO, fnic, "Received ABTS rsp with unknown oxid(0x%x) from 0x%x. Dropping frame", @@ -5085,6 +5128,9 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame, case FNIC_FABRIC_BLS_ABTS_RSP: fdls_process_fabric_abts_rsp(iport, fchdr); break; + case FNIC_LS_REQ_ABTS_RSP: + nvfnic_process_ls_abts_rsp(iport, fchdr); + break; case FNIC_FDMI_BLS_ABTS_RSP: fdls_process_fdmi_abts_rsp(iport, fchdr); break; diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 94b7c150c08c..b00672ef8b00 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -31,6 +31,11 @@ struct workqueue_struct *fnic_event_queue; static uint8_t FCOE_ALL_FCF_MAC[6] = FC_FCOE_FLOGI_MAC; +static inline bool fnic_is_nvme_frame(struct fc_frame_header *fchdr) +{ + return (fchdr->fh_type == FC_TYPE_NVME); +} + /* * Internal Functions * This function will initialize the src_mac address to be @@ -284,6 +289,7 @@ void fnic_handle_frame(struct work_struct *work) struct fnic *fnic = container_of(work, struct fnic, frame_work); struct fnic_frame_list *cur_frame, *next; int fchdr_offset = 0; + struct fc_frame_header *fchdr; spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); list_for_each_entry_safe(cur_frame, next, &fnic->frame_queue, links) { @@ -313,8 +319,14 @@ void fnic_handle_frame(struct work_struct *work) fchdr_offset = (cur_frame->rx_ethhdr_stripped) ? 0 : FNIC_ETH_FCOE_HDRS_OFFSET; - fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp, - cur_frame->frame_len, fchdr_offset); + fchdr = (struct fc_frame_header *)((u8 *)cur_frame->fp + fchdr_offset); + if (IS_FNIC_NVME_INITIATOR(fnic) && fnic_is_nvme_frame(fchdr)) { + nvfnic_ls_rsp_recv(&fnic->iport, fchdr, + cur_frame->frame_len - fchdr_offset); + } else { + fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp, + cur_frame->frame_len, fchdr_offset); + } mempool_free(cur_frame->fp, fnic->frame_recv_pool); mempool_free(cur_frame, fnic->frame_elem_pool); @@ -614,9 +626,13 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) { - void *rq_buf = buf->os_buf; + void *rq_buf; struct fnic *fnic = vnic_dev_priv(rq->vdev); + if (WARN_ON(!buf)) + return; + + rq_buf = buf->os_buf; dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, DMA_FROM_DEVICE); @@ -651,7 +667,7 @@ static int fnic_send_frame(struct fnic *fnic, void *frame, int frame_len) dma_unmap_single(&fnic->pdev->dev, pa, frame_len, DMA_TO_DEVICE); FNIC_FCS_DBG(KERN_INFO, fnic, "vnic work queue descriptor is not available"); - ret = -1; + ret = -ENXIO; goto fnic_send_frame_end; } @@ -685,7 +701,6 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size, struct fcoe_hdr *pfcoe_hdr; struct fnic_frame_list *frame_elem; int len = frame_size; - int ret; struct fc_frame_header *fchdr = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET); @@ -723,8 +738,7 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size, fnic_debug_dump_fc_frame(fnic, fchdr, frame_size, "Outgoing"); - ret = fnic_send_frame(fnic, frame, len); - return ret; + return fnic_send_frame(fnic, frame, len); } int fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame, @@ -872,6 +886,9 @@ static void fnic_wq_complete_frame_send(struct vnic_wq *wq, { struct fnic *fnic = vnic_dev_priv(wq->vdev); + if (WARN_ON(!buf)) + return; + dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, DMA_TO_DEVICE); mempool_free(buf->os_buf, fnic->frame_pool); @@ -914,6 +931,9 @@ void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) { struct fnic *fnic = vnic_dev_priv(wq->vdev); + if (WARN_ON(!buf)) + return; + dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, DMA_TO_DEVICE);