162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016 Avago Technologies. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/blk-mq.h> 962306a36Sopenharmony_ci#include <linux/parser.h> 1062306a36Sopenharmony_ci#include <linux/random.h> 1162306a36Sopenharmony_ci#include <uapi/scsi/fc/fc_fs.h> 1262306a36Sopenharmony_ci#include <uapi/scsi/fc/fc_els.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "nvmet.h" 1562306a36Sopenharmony_ci#include <linux/nvme-fc-driver.h> 1662306a36Sopenharmony_ci#include <linux/nvme-fc.h> 1762306a36Sopenharmony_ci#include "../host/fc.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* *************************** Data Structures/Defines ****************** */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define NVMET_LS_CTX_COUNT 256 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct nvmet_fc_tgtport; 2662306a36Sopenharmony_cistruct nvmet_fc_tgt_assoc; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct nvmet_fc_ls_iod { /* for an LS RQST RCV */ 2962306a36Sopenharmony_ci struct nvmefc_ls_rsp *lsrsp; 3062306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq; /* only if RS */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci struct list_head ls_rcv_list; /* tgtport->ls_rcv_list */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport; 3562306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc; 3662306a36Sopenharmony_ci void *hosthandle; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci union nvmefc_ls_requests *rqstbuf; 3962306a36Sopenharmony_ci union nvmefc_ls_responses *rspbuf; 4062306a36Sopenharmony_ci u16 rqstdatalen; 4162306a36Sopenharmony_ci dma_addr_t rspdma; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci struct scatterlist sg[2]; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci struct work_struct work; 4662306a36Sopenharmony_ci} __aligned(sizeof(unsigned long long)); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct nvmet_fc_ls_req_op { /* for an LS RQST XMT */ 4962306a36Sopenharmony_ci struct nvmefc_ls_req ls_req; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport; 5262306a36Sopenharmony_ci void *hosthandle; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci int ls_error; 5562306a36Sopenharmony_ci struct list_head lsreq_list; /* tgtport->ls_req_list */ 5662306a36Sopenharmony_ci bool req_queued; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* desired maximum for a single sequence - if sg list allows it */ 6162306a36Sopenharmony_ci#define NVMET_FC_MAX_SEQ_LENGTH (256 * 1024) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cienum nvmet_fcp_datadir { 6462306a36Sopenharmony_ci NVMET_FCP_NODATA, 6562306a36Sopenharmony_ci NVMET_FCP_WRITE, 6662306a36Sopenharmony_ci NVMET_FCP_READ, 6762306a36Sopenharmony_ci NVMET_FCP_ABORTED, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct nvmet_fc_fcp_iod { 7162306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci struct nvme_fc_cmd_iu cmdiubuf; 7462306a36Sopenharmony_ci struct nvme_fc_ersp_iu rspiubuf; 7562306a36Sopenharmony_ci dma_addr_t rspdma; 7662306a36Sopenharmony_ci struct scatterlist *next_sg; 7762306a36Sopenharmony_ci struct scatterlist *data_sg; 7862306a36Sopenharmony_ci int data_sg_cnt; 7962306a36Sopenharmony_ci u32 offset; 8062306a36Sopenharmony_ci enum nvmet_fcp_datadir io_dir; 8162306a36Sopenharmony_ci bool active; 8262306a36Sopenharmony_ci bool abort; 8362306a36Sopenharmony_ci bool aborted; 8462306a36Sopenharmony_ci bool writedataactive; 8562306a36Sopenharmony_ci spinlock_t flock; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci struct nvmet_req req; 8862306a36Sopenharmony_ci struct work_struct defer_work; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport; 9162306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci struct list_head fcp_list; /* tgtport->fcp_list */ 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct nvmet_fc_tgtport { 9762306a36Sopenharmony_ci struct nvmet_fc_target_port fc_target_port; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci struct list_head tgt_list; /* nvmet_fc_target_list */ 10062306a36Sopenharmony_ci struct device *dev; /* dev for dma mapping */ 10162306a36Sopenharmony_ci struct nvmet_fc_target_template *ops; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod; 10462306a36Sopenharmony_ci spinlock_t lock; 10562306a36Sopenharmony_ci struct list_head ls_rcv_list; 10662306a36Sopenharmony_ci struct list_head ls_req_list; 10762306a36Sopenharmony_ci struct list_head ls_busylist; 10862306a36Sopenharmony_ci struct list_head assoc_list; 10962306a36Sopenharmony_ci struct list_head host_list; 11062306a36Sopenharmony_ci struct ida assoc_cnt; 11162306a36Sopenharmony_ci struct nvmet_fc_port_entry *pe; 11262306a36Sopenharmony_ci struct kref ref; 11362306a36Sopenharmony_ci u32 max_sg_cnt; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci struct work_struct put_work; 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistruct nvmet_fc_port_entry { 11962306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport; 12062306a36Sopenharmony_ci struct nvmet_port *port; 12162306a36Sopenharmony_ci u64 node_name; 12262306a36Sopenharmony_ci u64 port_name; 12362306a36Sopenharmony_ci struct list_head pe_list; 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistruct nvmet_fc_defer_fcp_req { 12762306a36Sopenharmony_ci struct list_head req_list; 12862306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcp_req; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct nvmet_fc_tgt_queue { 13262306a36Sopenharmony_ci bool ninetypercent; 13362306a36Sopenharmony_ci u16 qid; 13462306a36Sopenharmony_ci u16 sqsize; 13562306a36Sopenharmony_ci u16 ersp_ratio; 13662306a36Sopenharmony_ci __le16 sqhd; 13762306a36Sopenharmony_ci atomic_t connected; 13862306a36Sopenharmony_ci atomic_t sqtail; 13962306a36Sopenharmony_ci atomic_t zrspcnt; 14062306a36Sopenharmony_ci atomic_t rsn; 14162306a36Sopenharmony_ci spinlock_t qlock; 14262306a36Sopenharmony_ci struct nvmet_cq nvme_cq; 14362306a36Sopenharmony_ci struct nvmet_sq nvme_sq; 14462306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc; 14562306a36Sopenharmony_ci struct list_head fod_list; 14662306a36Sopenharmony_ci struct list_head pending_cmd_list; 14762306a36Sopenharmony_ci struct list_head avail_defer_list; 14862306a36Sopenharmony_ci struct workqueue_struct *work_q; 14962306a36Sopenharmony_ci struct kref ref; 15062306a36Sopenharmony_ci struct rcu_head rcu; 15162306a36Sopenharmony_ci struct nvmet_fc_fcp_iod fod[]; /* array of fcp_iods */ 15262306a36Sopenharmony_ci} __aligned(sizeof(unsigned long long)); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistruct nvmet_fc_hostport { 15562306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport; 15662306a36Sopenharmony_ci void *hosthandle; 15762306a36Sopenharmony_ci struct list_head host_list; 15862306a36Sopenharmony_ci struct kref ref; 15962306a36Sopenharmony_ci u8 invalid; 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistruct nvmet_fc_tgt_assoc { 16362306a36Sopenharmony_ci u64 association_id; 16462306a36Sopenharmony_ci u32 a_id; 16562306a36Sopenharmony_ci atomic_t terminating; 16662306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport; 16762306a36Sopenharmony_ci struct nvmet_fc_hostport *hostport; 16862306a36Sopenharmony_ci struct nvmet_fc_ls_iod *rcv_disconn; 16962306a36Sopenharmony_ci struct list_head a_list; 17062306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queues[NVMET_NR_QUEUES + 1]; 17162306a36Sopenharmony_ci struct kref ref; 17262306a36Sopenharmony_ci struct work_struct del_work; 17362306a36Sopenharmony_ci struct rcu_head rcu; 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic inline int 17862306a36Sopenharmony_cinvmet_fc_iodnum(struct nvmet_fc_ls_iod *iodptr) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci return (iodptr - iodptr->tgtport->iod); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline int 18462306a36Sopenharmony_cinvmet_fc_fodnum(struct nvmet_fc_fcp_iod *fodptr) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci return (fodptr - fodptr->queue->fod); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * Association and Connection IDs: 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Association ID will have random number in upper 6 bytes and zero 19462306a36Sopenharmony_ci * in lower 2 bytes 19562306a36Sopenharmony_ci * 19662306a36Sopenharmony_ci * Connection IDs will be Association ID with QID or'd in lower 2 bytes 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * note: Association ID = Connection ID for queue 0 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci#define BYTES_FOR_QID sizeof(u16) 20162306a36Sopenharmony_ci#define BYTES_FOR_QID_SHIFT (BYTES_FOR_QID * 8) 20262306a36Sopenharmony_ci#define NVMET_FC_QUEUEID_MASK ((u64)((1 << BYTES_FOR_QID_SHIFT) - 1)) 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic inline u64 20562306a36Sopenharmony_cinvmet_fc_makeconnid(struct nvmet_fc_tgt_assoc *assoc, u16 qid) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci return (assoc->association_id | qid); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic inline u64 21162306a36Sopenharmony_cinvmet_fc_getassociationid(u64 connectionid) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci return connectionid & ~NVMET_FC_QUEUEID_MASK; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic inline u16 21762306a36Sopenharmony_cinvmet_fc_getqueueid(u64 connectionid) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci return (u16)(connectionid & NVMET_FC_QUEUEID_MASK); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic inline struct nvmet_fc_tgtport * 22362306a36Sopenharmony_citargetport_to_tgtport(struct nvmet_fc_target_port *targetport) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci return container_of(targetport, struct nvmet_fc_tgtport, 22662306a36Sopenharmony_ci fc_target_port); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic inline struct nvmet_fc_fcp_iod * 23062306a36Sopenharmony_cinvmet_req_to_fod(struct nvmet_req *nvme_req) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci return container_of(nvme_req, struct nvmet_fc_fcp_iod, req); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* *************************** Globals **************************** */ 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nvmet_fc_tgtlock); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic LIST_HEAD(nvmet_fc_target_list); 24262306a36Sopenharmony_cistatic DEFINE_IDA(nvmet_fc_tgtport_cnt); 24362306a36Sopenharmony_cistatic LIST_HEAD(nvmet_fc_portentry_list); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void nvmet_fc_handle_ls_rqst_work(struct work_struct *work); 24762306a36Sopenharmony_cistatic void nvmet_fc_fcp_rqst_op_defer_work(struct work_struct *work); 24862306a36Sopenharmony_cistatic void nvmet_fc_tgt_a_put(struct nvmet_fc_tgt_assoc *assoc); 24962306a36Sopenharmony_cistatic int nvmet_fc_tgt_a_get(struct nvmet_fc_tgt_assoc *assoc); 25062306a36Sopenharmony_cistatic void nvmet_fc_tgt_q_put(struct nvmet_fc_tgt_queue *queue); 25162306a36Sopenharmony_cistatic int nvmet_fc_tgt_q_get(struct nvmet_fc_tgt_queue *queue); 25262306a36Sopenharmony_cistatic void nvmet_fc_tgtport_put(struct nvmet_fc_tgtport *tgtport); 25362306a36Sopenharmony_cistatic void nvmet_fc_put_tgtport_work(struct work_struct *work) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = 25662306a36Sopenharmony_ci container_of(work, struct nvmet_fc_tgtport, put_work); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_cistatic int nvmet_fc_tgtport_get(struct nvmet_fc_tgtport *tgtport); 26162306a36Sopenharmony_cistatic void nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, 26262306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod); 26362306a36Sopenharmony_cistatic void nvmet_fc_delete_target_assoc(struct nvmet_fc_tgt_assoc *assoc); 26462306a36Sopenharmony_cistatic void nvmet_fc_xmt_ls_rsp(struct nvmet_fc_tgtport *tgtport, 26562306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* *********************** FC-NVME DMA Handling **************************** */ 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * The fcloop device passes in a NULL device pointer. Real LLD's will 27262306a36Sopenharmony_ci * pass in a valid device pointer. If NULL is passed to the dma mapping 27362306a36Sopenharmony_ci * routines, depending on the platform, it may or may not succeed, and 27462306a36Sopenharmony_ci * may crash. 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * As such: 27762306a36Sopenharmony_ci * Wrapper all the dma routines and check the dev pointer. 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * If simple mappings (return just a dma address, we'll noop them, 28062306a36Sopenharmony_ci * returning a dma address of 0. 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * On more complex mappings (dma_map_sg), a pseudo routine fills 28362306a36Sopenharmony_ci * in the scatter list, setting all dma addresses to 0. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic inline dma_addr_t 28762306a36Sopenharmony_cifc_dma_map_single(struct device *dev, void *ptr, size_t size, 28862306a36Sopenharmony_ci enum dma_data_direction dir) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci return dev ? dma_map_single(dev, ptr, size, dir) : (dma_addr_t)0L; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic inline int 29462306a36Sopenharmony_cifc_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci return dev ? dma_mapping_error(dev, dma_addr) : 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic inline void 30062306a36Sopenharmony_cifc_dma_unmap_single(struct device *dev, dma_addr_t addr, size_t size, 30162306a36Sopenharmony_ci enum dma_data_direction dir) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci if (dev) 30462306a36Sopenharmony_ci dma_unmap_single(dev, addr, size, dir); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic inline void 30862306a36Sopenharmony_cifc_dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, size_t size, 30962306a36Sopenharmony_ci enum dma_data_direction dir) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci if (dev) 31262306a36Sopenharmony_ci dma_sync_single_for_cpu(dev, addr, size, dir); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic inline void 31662306a36Sopenharmony_cifc_dma_sync_single_for_device(struct device *dev, dma_addr_t addr, size_t size, 31762306a36Sopenharmony_ci enum dma_data_direction dir) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci if (dev) 32062306a36Sopenharmony_ci dma_sync_single_for_device(dev, addr, size, dir); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci/* pseudo dma_map_sg call */ 32462306a36Sopenharmony_cistatic int 32562306a36Sopenharmony_cifc_map_sg(struct scatterlist *sg, int nents) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct scatterlist *s; 32862306a36Sopenharmony_ci int i; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci WARN_ON(nents == 0 || sg[0].length == 0); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci for_each_sg(sg, s, nents, i) { 33362306a36Sopenharmony_ci s->dma_address = 0L; 33462306a36Sopenharmony_ci#ifdef CONFIG_NEED_SG_DMA_LENGTH 33562306a36Sopenharmony_ci s->dma_length = s->length; 33662306a36Sopenharmony_ci#endif 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci return nents; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic inline int 34262306a36Sopenharmony_cifc_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, 34362306a36Sopenharmony_ci enum dma_data_direction dir) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci return dev ? dma_map_sg(dev, sg, nents, dir) : fc_map_sg(sg, nents); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic inline void 34962306a36Sopenharmony_cifc_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, 35062306a36Sopenharmony_ci enum dma_data_direction dir) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci if (dev) 35362306a36Sopenharmony_ci dma_unmap_sg(dev, sg, nents, dir); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* ********************** FC-NVME LS XMT Handling ************************* */ 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void 36162306a36Sopenharmony_ci__nvmet_fc_finish_ls_req(struct nvmet_fc_ls_req_op *lsop) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = lsop->tgtport; 36462306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq = &lsop->ls_req; 36562306a36Sopenharmony_ci unsigned long flags; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (!lsop->req_queued) { 37062306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 37162306a36Sopenharmony_ci goto out_putwork; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci list_del(&lsop->lsreq_list); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci lsop->req_queued = false; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci fc_dma_unmap_single(tgtport->dev, lsreq->rqstdma, 38162306a36Sopenharmony_ci (lsreq->rqstlen + lsreq->rsplen), 38262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ciout_putwork: 38562306a36Sopenharmony_ci queue_work(nvmet_wq, &tgtport->put_work); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int 38962306a36Sopenharmony_ci__nvmet_fc_send_ls_req(struct nvmet_fc_tgtport *tgtport, 39062306a36Sopenharmony_ci struct nvmet_fc_ls_req_op *lsop, 39162306a36Sopenharmony_ci void (*done)(struct nvmefc_ls_req *req, int status)) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq = &lsop->ls_req; 39462306a36Sopenharmony_ci unsigned long flags; 39562306a36Sopenharmony_ci int ret = 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (!tgtport->ops->ls_req) 39862306a36Sopenharmony_ci return -EOPNOTSUPP; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (!nvmet_fc_tgtport_get(tgtport)) 40162306a36Sopenharmony_ci return -ESHUTDOWN; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci lsreq->done = done; 40462306a36Sopenharmony_ci lsop->req_queued = false; 40562306a36Sopenharmony_ci INIT_LIST_HEAD(&lsop->lsreq_list); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci lsreq->rqstdma = fc_dma_map_single(tgtport->dev, lsreq->rqstaddr, 40862306a36Sopenharmony_ci lsreq->rqstlen + lsreq->rsplen, 40962306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 41062306a36Sopenharmony_ci if (fc_dma_mapping_error(tgtport->dev, lsreq->rqstdma)) { 41162306a36Sopenharmony_ci ret = -EFAULT; 41262306a36Sopenharmony_ci goto out_puttgtport; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci lsreq->rspdma = lsreq->rqstdma + lsreq->rqstlen; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci list_add_tail(&lsop->lsreq_list, &tgtport->ls_req_list); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci lsop->req_queued = true; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci ret = tgtport->ops->ls_req(&tgtport->fc_target_port, lsop->hosthandle, 42562306a36Sopenharmony_ci lsreq); 42662306a36Sopenharmony_ci if (ret) 42762306a36Sopenharmony_ci goto out_unlink; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ciout_unlink: 43262306a36Sopenharmony_ci lsop->ls_error = ret; 43362306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 43462306a36Sopenharmony_ci lsop->req_queued = false; 43562306a36Sopenharmony_ci list_del(&lsop->lsreq_list); 43662306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 43762306a36Sopenharmony_ci fc_dma_unmap_single(tgtport->dev, lsreq->rqstdma, 43862306a36Sopenharmony_ci (lsreq->rqstlen + lsreq->rsplen), 43962306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 44062306a36Sopenharmony_ciout_puttgtport: 44162306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return ret; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int 44762306a36Sopenharmony_cinvmet_fc_send_ls_req_async(struct nvmet_fc_tgtport *tgtport, 44862306a36Sopenharmony_ci struct nvmet_fc_ls_req_op *lsop, 44962306a36Sopenharmony_ci void (*done)(struct nvmefc_ls_req *req, int status)) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci /* don't wait for completion */ 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return __nvmet_fc_send_ls_req(tgtport, lsop, done); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic void 45762306a36Sopenharmony_cinvmet_fc_disconnect_assoc_done(struct nvmefc_ls_req *lsreq, int status) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct nvmet_fc_ls_req_op *lsop = 46062306a36Sopenharmony_ci container_of(lsreq, struct nvmet_fc_ls_req_op, ls_req); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci __nvmet_fc_finish_ls_req(lsop); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* fc-nvme target doesn't care about success or failure of cmd */ 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci kfree(lsop); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci/* 47062306a36Sopenharmony_ci * This routine sends a FC-NVME LS to disconnect (aka terminate) 47162306a36Sopenharmony_ci * the FC-NVME Association. Terminating the association also 47262306a36Sopenharmony_ci * terminates the FC-NVME connections (per queue, both admin and io 47362306a36Sopenharmony_ci * queues) that are part of the association. E.g. things are torn 47462306a36Sopenharmony_ci * down, and the related FC-NVME Association ID and Connection IDs 47562306a36Sopenharmony_ci * become invalid. 47662306a36Sopenharmony_ci * 47762306a36Sopenharmony_ci * The behavior of the fc-nvme target is such that it's 47862306a36Sopenharmony_ci * understanding of the association and connections will implicitly 47962306a36Sopenharmony_ci * be torn down. The action is implicit as it may be due to a loss of 48062306a36Sopenharmony_ci * connectivity with the fc-nvme host, so the target may never get a 48162306a36Sopenharmony_ci * response even if it tried. As such, the action of this routine 48262306a36Sopenharmony_ci * is to asynchronously send the LS, ignore any results of the LS, and 48362306a36Sopenharmony_ci * continue on with terminating the association. If the fc-nvme host 48462306a36Sopenharmony_ci * is present and receives the LS, it too can tear down. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_cistatic void 48762306a36Sopenharmony_cinvmet_fc_xmt_disconnect_assoc(struct nvmet_fc_tgt_assoc *assoc) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = assoc->tgtport; 49062306a36Sopenharmony_ci struct fcnvme_ls_disconnect_assoc_rqst *discon_rqst; 49162306a36Sopenharmony_ci struct fcnvme_ls_disconnect_assoc_acc *discon_acc; 49262306a36Sopenharmony_ci struct nvmet_fc_ls_req_op *lsop; 49362306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq; 49462306a36Sopenharmony_ci int ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* 49762306a36Sopenharmony_ci * If ls_req is NULL or no hosthandle, it's an older lldd and no 49862306a36Sopenharmony_ci * message is normal. Otherwise, send unless the hostport has 49962306a36Sopenharmony_ci * already been invalidated by the lldd. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ci if (!tgtport->ops->ls_req || !assoc->hostport || 50262306a36Sopenharmony_ci assoc->hostport->invalid) 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci lsop = kzalloc((sizeof(*lsop) + 50662306a36Sopenharmony_ci sizeof(*discon_rqst) + sizeof(*discon_acc) + 50762306a36Sopenharmony_ci tgtport->ops->lsrqst_priv_sz), GFP_KERNEL); 50862306a36Sopenharmony_ci if (!lsop) { 50962306a36Sopenharmony_ci dev_info(tgtport->dev, 51062306a36Sopenharmony_ci "{%d:%d} send Disconnect Association failed: ENOMEM\n", 51162306a36Sopenharmony_ci tgtport->fc_target_port.port_num, assoc->a_id); 51262306a36Sopenharmony_ci return; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci discon_rqst = (struct fcnvme_ls_disconnect_assoc_rqst *)&lsop[1]; 51662306a36Sopenharmony_ci discon_acc = (struct fcnvme_ls_disconnect_assoc_acc *)&discon_rqst[1]; 51762306a36Sopenharmony_ci lsreq = &lsop->ls_req; 51862306a36Sopenharmony_ci if (tgtport->ops->lsrqst_priv_sz) 51962306a36Sopenharmony_ci lsreq->private = (void *)&discon_acc[1]; 52062306a36Sopenharmony_ci else 52162306a36Sopenharmony_ci lsreq->private = NULL; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci lsop->tgtport = tgtport; 52462306a36Sopenharmony_ci lsop->hosthandle = assoc->hostport->hosthandle; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci nvmefc_fmt_lsreq_discon_assoc(lsreq, discon_rqst, discon_acc, 52762306a36Sopenharmony_ci assoc->association_id); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ret = nvmet_fc_send_ls_req_async(tgtport, lsop, 53062306a36Sopenharmony_ci nvmet_fc_disconnect_assoc_done); 53162306a36Sopenharmony_ci if (ret) { 53262306a36Sopenharmony_ci dev_info(tgtport->dev, 53362306a36Sopenharmony_ci "{%d:%d} XMT Disconnect Association failed: %d\n", 53462306a36Sopenharmony_ci tgtport->fc_target_port.port_num, assoc->a_id, ret); 53562306a36Sopenharmony_ci kfree(lsop); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* *********************** FC-NVME Port Management ************************ */ 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int 54462306a36Sopenharmony_cinvmet_fc_alloc_ls_iodlist(struct nvmet_fc_tgtport *tgtport) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod; 54762306a36Sopenharmony_ci int i; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci iod = kcalloc(NVMET_LS_CTX_COUNT, sizeof(struct nvmet_fc_ls_iod), 55062306a36Sopenharmony_ci GFP_KERNEL); 55162306a36Sopenharmony_ci if (!iod) 55262306a36Sopenharmony_ci return -ENOMEM; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci tgtport->iod = iod; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci for (i = 0; i < NVMET_LS_CTX_COUNT; iod++, i++) { 55762306a36Sopenharmony_ci INIT_WORK(&iod->work, nvmet_fc_handle_ls_rqst_work); 55862306a36Sopenharmony_ci iod->tgtport = tgtport; 55962306a36Sopenharmony_ci list_add_tail(&iod->ls_rcv_list, &tgtport->ls_rcv_list); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci iod->rqstbuf = kzalloc(sizeof(union nvmefc_ls_requests) + 56262306a36Sopenharmony_ci sizeof(union nvmefc_ls_responses), 56362306a36Sopenharmony_ci GFP_KERNEL); 56462306a36Sopenharmony_ci if (!iod->rqstbuf) 56562306a36Sopenharmony_ci goto out_fail; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci iod->rspbuf = (union nvmefc_ls_responses *)&iod->rqstbuf[1]; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci iod->rspdma = fc_dma_map_single(tgtport->dev, iod->rspbuf, 57062306a36Sopenharmony_ci sizeof(*iod->rspbuf), 57162306a36Sopenharmony_ci DMA_TO_DEVICE); 57262306a36Sopenharmony_ci if (fc_dma_mapping_error(tgtport->dev, iod->rspdma)) 57362306a36Sopenharmony_ci goto out_fail; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ciout_fail: 57962306a36Sopenharmony_ci kfree(iod->rqstbuf); 58062306a36Sopenharmony_ci list_del(&iod->ls_rcv_list); 58162306a36Sopenharmony_ci for (iod--, i--; i >= 0; iod--, i--) { 58262306a36Sopenharmony_ci fc_dma_unmap_single(tgtport->dev, iod->rspdma, 58362306a36Sopenharmony_ci sizeof(*iod->rspbuf), DMA_TO_DEVICE); 58462306a36Sopenharmony_ci kfree(iod->rqstbuf); 58562306a36Sopenharmony_ci list_del(&iod->ls_rcv_list); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci kfree(iod); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return -EFAULT; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic void 59462306a36Sopenharmony_cinvmet_fc_free_ls_iodlist(struct nvmet_fc_tgtport *tgtport) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod = tgtport->iod; 59762306a36Sopenharmony_ci int i; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci for (i = 0; i < NVMET_LS_CTX_COUNT; iod++, i++) { 60062306a36Sopenharmony_ci fc_dma_unmap_single(tgtport->dev, 60162306a36Sopenharmony_ci iod->rspdma, sizeof(*iod->rspbuf), 60262306a36Sopenharmony_ci DMA_TO_DEVICE); 60362306a36Sopenharmony_ci kfree(iod->rqstbuf); 60462306a36Sopenharmony_ci list_del(&iod->ls_rcv_list); 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci kfree(tgtport->iod); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic struct nvmet_fc_ls_iod * 61062306a36Sopenharmony_cinvmet_fc_alloc_ls_iod(struct nvmet_fc_tgtport *tgtport) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod; 61362306a36Sopenharmony_ci unsigned long flags; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 61662306a36Sopenharmony_ci iod = list_first_entry_or_null(&tgtport->ls_rcv_list, 61762306a36Sopenharmony_ci struct nvmet_fc_ls_iod, ls_rcv_list); 61862306a36Sopenharmony_ci if (iod) 61962306a36Sopenharmony_ci list_move_tail(&iod->ls_rcv_list, &tgtport->ls_busylist); 62062306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 62162306a36Sopenharmony_ci return iod; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void 62662306a36Sopenharmony_cinvmet_fc_free_ls_iod(struct nvmet_fc_tgtport *tgtport, 62762306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci unsigned long flags; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 63262306a36Sopenharmony_ci list_move(&iod->ls_rcv_list, &tgtport->ls_rcv_list); 63362306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic void 63762306a36Sopenharmony_cinvmet_fc_prep_fcp_iodlist(struct nvmet_fc_tgtport *tgtport, 63862306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod = queue->fod; 64162306a36Sopenharmony_ci int i; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci for (i = 0; i < queue->sqsize; fod++, i++) { 64462306a36Sopenharmony_ci INIT_WORK(&fod->defer_work, nvmet_fc_fcp_rqst_op_defer_work); 64562306a36Sopenharmony_ci fod->tgtport = tgtport; 64662306a36Sopenharmony_ci fod->queue = queue; 64762306a36Sopenharmony_ci fod->active = false; 64862306a36Sopenharmony_ci fod->abort = false; 64962306a36Sopenharmony_ci fod->aborted = false; 65062306a36Sopenharmony_ci fod->fcpreq = NULL; 65162306a36Sopenharmony_ci list_add_tail(&fod->fcp_list, &queue->fod_list); 65262306a36Sopenharmony_ci spin_lock_init(&fod->flock); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci fod->rspdma = fc_dma_map_single(tgtport->dev, &fod->rspiubuf, 65562306a36Sopenharmony_ci sizeof(fod->rspiubuf), DMA_TO_DEVICE); 65662306a36Sopenharmony_ci if (fc_dma_mapping_error(tgtport->dev, fod->rspdma)) { 65762306a36Sopenharmony_ci list_del(&fod->fcp_list); 65862306a36Sopenharmony_ci for (fod--, i--; i >= 0; fod--, i--) { 65962306a36Sopenharmony_ci fc_dma_unmap_single(tgtport->dev, fod->rspdma, 66062306a36Sopenharmony_ci sizeof(fod->rspiubuf), 66162306a36Sopenharmony_ci DMA_TO_DEVICE); 66262306a36Sopenharmony_ci fod->rspdma = 0L; 66362306a36Sopenharmony_ci list_del(&fod->fcp_list); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic void 67262306a36Sopenharmony_cinvmet_fc_destroy_fcp_iodlist(struct nvmet_fc_tgtport *tgtport, 67362306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod = queue->fod; 67662306a36Sopenharmony_ci int i; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci for (i = 0; i < queue->sqsize; fod++, i++) { 67962306a36Sopenharmony_ci if (fod->rspdma) 68062306a36Sopenharmony_ci fc_dma_unmap_single(tgtport->dev, fod->rspdma, 68162306a36Sopenharmony_ci sizeof(fod->rspiubuf), DMA_TO_DEVICE); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic struct nvmet_fc_fcp_iod * 68662306a36Sopenharmony_cinvmet_fc_alloc_fcp_iod(struct nvmet_fc_tgt_queue *queue) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci lockdep_assert_held(&queue->qlock); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci fod = list_first_entry_or_null(&queue->fod_list, 69362306a36Sopenharmony_ci struct nvmet_fc_fcp_iod, fcp_list); 69462306a36Sopenharmony_ci if (fod) { 69562306a36Sopenharmony_ci list_del(&fod->fcp_list); 69662306a36Sopenharmony_ci fod->active = true; 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * no queue reference is taken, as it was taken by the 69962306a36Sopenharmony_ci * queue lookup just prior to the allocation. The iod 70062306a36Sopenharmony_ci * will "inherit" that reference. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci return fod; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic void 70862306a36Sopenharmony_cinvmet_fc_queue_fcp_req(struct nvmet_fc_tgtport *tgtport, 70962306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue, 71062306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod = fcpreq->nvmet_fc_private; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* 71562306a36Sopenharmony_ci * put all admin cmds on hw queue id 0. All io commands go to 71662306a36Sopenharmony_ci * the respective hw queue based on a modulo basis 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci fcpreq->hwqid = queue->qid ? 71962306a36Sopenharmony_ci ((queue->qid - 1) % tgtport->ops->max_hw_queues) : 0; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci nvmet_fc_handle_fcp_rqst(tgtport, fod); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic void 72562306a36Sopenharmony_cinvmet_fc_fcp_rqst_op_defer_work(struct work_struct *work) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod = 72862306a36Sopenharmony_ci container_of(work, struct nvmet_fc_fcp_iod, defer_work); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Submit deferred IO for processing */ 73162306a36Sopenharmony_ci nvmet_fc_queue_fcp_req(fod->tgtport, fod->queue, fod->fcpreq); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic void 73662306a36Sopenharmony_cinvmet_fc_free_fcp_iod(struct nvmet_fc_tgt_queue *queue, 73762306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq = fod->fcpreq; 74062306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = fod->tgtport; 74162306a36Sopenharmony_ci struct nvmet_fc_defer_fcp_req *deferfcp; 74262306a36Sopenharmony_ci unsigned long flags; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci fc_dma_sync_single_for_cpu(tgtport->dev, fod->rspdma, 74562306a36Sopenharmony_ci sizeof(fod->rspiubuf), DMA_TO_DEVICE); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci fcpreq->nvmet_fc_private = NULL; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci fod->active = false; 75062306a36Sopenharmony_ci fod->abort = false; 75162306a36Sopenharmony_ci fod->aborted = false; 75262306a36Sopenharmony_ci fod->writedataactive = false; 75362306a36Sopenharmony_ci fod->fcpreq = NULL; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci tgtport->ops->fcp_req_release(&tgtport->fc_target_port, fcpreq); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* release the queue lookup reference on the completed IO */ 75862306a36Sopenharmony_ci nvmet_fc_tgt_q_put(queue); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci spin_lock_irqsave(&queue->qlock, flags); 76162306a36Sopenharmony_ci deferfcp = list_first_entry_or_null(&queue->pending_cmd_list, 76262306a36Sopenharmony_ci struct nvmet_fc_defer_fcp_req, req_list); 76362306a36Sopenharmony_ci if (!deferfcp) { 76462306a36Sopenharmony_ci list_add_tail(&fod->fcp_list, &fod->queue->fod_list); 76562306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 76662306a36Sopenharmony_ci return; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Re-use the fod for the next pending cmd that was deferred */ 77062306a36Sopenharmony_ci list_del(&deferfcp->req_list); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci fcpreq = deferfcp->fcp_req; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* deferfcp can be reused for another IO at a later date */ 77562306a36Sopenharmony_ci list_add_tail(&deferfcp->req_list, &queue->avail_defer_list); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Save NVME CMD IO in fod */ 78062306a36Sopenharmony_ci memcpy(&fod->cmdiubuf, fcpreq->rspaddr, fcpreq->rsplen); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* Setup new fcpreq to be processed */ 78362306a36Sopenharmony_ci fcpreq->rspaddr = NULL; 78462306a36Sopenharmony_ci fcpreq->rsplen = 0; 78562306a36Sopenharmony_ci fcpreq->nvmet_fc_private = fod; 78662306a36Sopenharmony_ci fod->fcpreq = fcpreq; 78762306a36Sopenharmony_ci fod->active = true; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* inform LLDD IO is now being processed */ 79062306a36Sopenharmony_ci tgtport->ops->defer_rcv(&tgtport->fc_target_port, fcpreq); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* 79362306a36Sopenharmony_ci * Leave the queue lookup get reference taken when 79462306a36Sopenharmony_ci * fod was originally allocated. 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci queue_work(queue->work_q, &fod->defer_work); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic struct nvmet_fc_tgt_queue * 80162306a36Sopenharmony_cinvmet_fc_alloc_target_queue(struct nvmet_fc_tgt_assoc *assoc, 80262306a36Sopenharmony_ci u16 qid, u16 sqsize) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue; 80562306a36Sopenharmony_ci int ret; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (qid > NVMET_NR_QUEUES) 80862306a36Sopenharmony_ci return NULL; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci queue = kzalloc(struct_size(queue, fod, sqsize), GFP_KERNEL); 81162306a36Sopenharmony_ci if (!queue) 81262306a36Sopenharmony_ci return NULL; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci queue->work_q = alloc_workqueue("ntfc%d.%d.%d", 0, 0, 81562306a36Sopenharmony_ci assoc->tgtport->fc_target_port.port_num, 81662306a36Sopenharmony_ci assoc->a_id, qid); 81762306a36Sopenharmony_ci if (!queue->work_q) 81862306a36Sopenharmony_ci goto out_free_queue; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci queue->qid = qid; 82162306a36Sopenharmony_ci queue->sqsize = sqsize; 82262306a36Sopenharmony_ci queue->assoc = assoc; 82362306a36Sopenharmony_ci INIT_LIST_HEAD(&queue->fod_list); 82462306a36Sopenharmony_ci INIT_LIST_HEAD(&queue->avail_defer_list); 82562306a36Sopenharmony_ci INIT_LIST_HEAD(&queue->pending_cmd_list); 82662306a36Sopenharmony_ci atomic_set(&queue->connected, 0); 82762306a36Sopenharmony_ci atomic_set(&queue->sqtail, 0); 82862306a36Sopenharmony_ci atomic_set(&queue->rsn, 1); 82962306a36Sopenharmony_ci atomic_set(&queue->zrspcnt, 0); 83062306a36Sopenharmony_ci spin_lock_init(&queue->qlock); 83162306a36Sopenharmony_ci kref_init(&queue->ref); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci nvmet_fc_prep_fcp_iodlist(assoc->tgtport, queue); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci ret = nvmet_sq_init(&queue->nvme_sq); 83662306a36Sopenharmony_ci if (ret) 83762306a36Sopenharmony_ci goto out_fail_iodlist; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci WARN_ON(assoc->queues[qid]); 84062306a36Sopenharmony_ci assoc->queues[qid] = queue; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci return queue; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ciout_fail_iodlist: 84562306a36Sopenharmony_ci nvmet_fc_destroy_fcp_iodlist(assoc->tgtport, queue); 84662306a36Sopenharmony_ci destroy_workqueue(queue->work_q); 84762306a36Sopenharmony_ciout_free_queue: 84862306a36Sopenharmony_ci kfree(queue); 84962306a36Sopenharmony_ci return NULL; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic void 85462306a36Sopenharmony_cinvmet_fc_tgt_queue_free(struct kref *ref) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue = 85762306a36Sopenharmony_ci container_of(ref, struct nvmet_fc_tgt_queue, ref); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci nvmet_fc_destroy_fcp_iodlist(queue->assoc->tgtport, queue); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci destroy_workqueue(queue->work_q); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci kfree_rcu(queue, rcu); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic void 86762306a36Sopenharmony_cinvmet_fc_tgt_q_put(struct nvmet_fc_tgt_queue *queue) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci kref_put(&queue->ref, nvmet_fc_tgt_queue_free); 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic int 87362306a36Sopenharmony_cinvmet_fc_tgt_q_get(struct nvmet_fc_tgt_queue *queue) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci return kref_get_unless_zero(&queue->ref); 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic void 88062306a36Sopenharmony_cinvmet_fc_delete_target_queue(struct nvmet_fc_tgt_queue *queue) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = queue->assoc->tgtport; 88362306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod = queue->fod; 88462306a36Sopenharmony_ci struct nvmet_fc_defer_fcp_req *deferfcp, *tempptr; 88562306a36Sopenharmony_ci unsigned long flags; 88662306a36Sopenharmony_ci int i; 88762306a36Sopenharmony_ci bool disconnect; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci disconnect = atomic_xchg(&queue->connected, 0); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* if not connected, nothing to do */ 89262306a36Sopenharmony_ci if (!disconnect) 89362306a36Sopenharmony_ci return; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci spin_lock_irqsave(&queue->qlock, flags); 89662306a36Sopenharmony_ci /* abort outstanding io's */ 89762306a36Sopenharmony_ci for (i = 0; i < queue->sqsize; fod++, i++) { 89862306a36Sopenharmony_ci if (fod->active) { 89962306a36Sopenharmony_ci spin_lock(&fod->flock); 90062306a36Sopenharmony_ci fod->abort = true; 90162306a36Sopenharmony_ci /* 90262306a36Sopenharmony_ci * only call lldd abort routine if waiting for 90362306a36Sopenharmony_ci * writedata. other outstanding ops should finish 90462306a36Sopenharmony_ci * on their own. 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_ci if (fod->writedataactive) { 90762306a36Sopenharmony_ci fod->aborted = true; 90862306a36Sopenharmony_ci spin_unlock(&fod->flock); 90962306a36Sopenharmony_ci tgtport->ops->fcp_abort( 91062306a36Sopenharmony_ci &tgtport->fc_target_port, fod->fcpreq); 91162306a36Sopenharmony_ci } else 91262306a36Sopenharmony_ci spin_unlock(&fod->flock); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Cleanup defer'ed IOs in queue */ 91762306a36Sopenharmony_ci list_for_each_entry_safe(deferfcp, tempptr, &queue->avail_defer_list, 91862306a36Sopenharmony_ci req_list) { 91962306a36Sopenharmony_ci list_del(&deferfcp->req_list); 92062306a36Sopenharmony_ci kfree(deferfcp); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci for (;;) { 92462306a36Sopenharmony_ci deferfcp = list_first_entry_or_null(&queue->pending_cmd_list, 92562306a36Sopenharmony_ci struct nvmet_fc_defer_fcp_req, req_list); 92662306a36Sopenharmony_ci if (!deferfcp) 92762306a36Sopenharmony_ci break; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci list_del(&deferfcp->req_list); 93062306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci tgtport->ops->defer_rcv(&tgtport->fc_target_port, 93362306a36Sopenharmony_ci deferfcp->fcp_req); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci tgtport->ops->fcp_abort(&tgtport->fc_target_port, 93662306a36Sopenharmony_ci deferfcp->fcp_req); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci tgtport->ops->fcp_req_release(&tgtport->fc_target_port, 93962306a36Sopenharmony_ci deferfcp->fcp_req); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* release the queue lookup reference */ 94262306a36Sopenharmony_ci nvmet_fc_tgt_q_put(queue); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci kfree(deferfcp); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci spin_lock_irqsave(&queue->qlock, flags); 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci flush_workqueue(queue->work_q); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci nvmet_sq_destroy(&queue->nvme_sq); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci nvmet_fc_tgt_q_put(queue); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic struct nvmet_fc_tgt_queue * 95862306a36Sopenharmony_cinvmet_fc_find_target_queue(struct nvmet_fc_tgtport *tgtport, 95962306a36Sopenharmony_ci u64 connection_id) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc; 96262306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue; 96362306a36Sopenharmony_ci u64 association_id = nvmet_fc_getassociationid(connection_id); 96462306a36Sopenharmony_ci u16 qid = nvmet_fc_getqueueid(connection_id); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (qid > NVMET_NR_QUEUES) 96762306a36Sopenharmony_ci return NULL; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci rcu_read_lock(); 97062306a36Sopenharmony_ci list_for_each_entry_rcu(assoc, &tgtport->assoc_list, a_list) { 97162306a36Sopenharmony_ci if (association_id == assoc->association_id) { 97262306a36Sopenharmony_ci queue = assoc->queues[qid]; 97362306a36Sopenharmony_ci if (queue && 97462306a36Sopenharmony_ci (!atomic_read(&queue->connected) || 97562306a36Sopenharmony_ci !nvmet_fc_tgt_q_get(queue))) 97662306a36Sopenharmony_ci queue = NULL; 97762306a36Sopenharmony_ci rcu_read_unlock(); 97862306a36Sopenharmony_ci return queue; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci rcu_read_unlock(); 98262306a36Sopenharmony_ci return NULL; 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_cistatic void 98662306a36Sopenharmony_cinvmet_fc_hostport_free(struct kref *ref) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct nvmet_fc_hostport *hostport = 98962306a36Sopenharmony_ci container_of(ref, struct nvmet_fc_hostport, ref); 99062306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = hostport->tgtport; 99162306a36Sopenharmony_ci unsigned long flags; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 99462306a36Sopenharmony_ci list_del(&hostport->host_list); 99562306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 99662306a36Sopenharmony_ci if (tgtport->ops->host_release && hostport->invalid) 99762306a36Sopenharmony_ci tgtport->ops->host_release(hostport->hosthandle); 99862306a36Sopenharmony_ci kfree(hostport); 99962306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic void 100362306a36Sopenharmony_cinvmet_fc_hostport_put(struct nvmet_fc_hostport *hostport) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci kref_put(&hostport->ref, nvmet_fc_hostport_free); 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic int 100962306a36Sopenharmony_cinvmet_fc_hostport_get(struct nvmet_fc_hostport *hostport) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci return kref_get_unless_zero(&hostport->ref); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic void 101562306a36Sopenharmony_cinvmet_fc_free_hostport(struct nvmet_fc_hostport *hostport) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci /* if LLDD not implemented, leave as NULL */ 101862306a36Sopenharmony_ci if (!hostport || !hostport->hosthandle) 101962306a36Sopenharmony_ci return; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci nvmet_fc_hostport_put(hostport); 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic struct nvmet_fc_hostport * 102562306a36Sopenharmony_cinvmet_fc_match_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct nvmet_fc_hostport *host; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci lockdep_assert_held(&tgtport->lock); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci list_for_each_entry(host, &tgtport->host_list, host_list) { 103262306a36Sopenharmony_ci if (host->hosthandle == hosthandle && !host->invalid) { 103362306a36Sopenharmony_ci if (nvmet_fc_hostport_get(host)) 103462306a36Sopenharmony_ci return (host); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci return NULL; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic struct nvmet_fc_hostport * 104262306a36Sopenharmony_cinvmet_fc_alloc_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct nvmet_fc_hostport *newhost, *match = NULL; 104562306a36Sopenharmony_ci unsigned long flags; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci /* if LLDD not implemented, leave as NULL */ 104862306a36Sopenharmony_ci if (!hosthandle) 104962306a36Sopenharmony_ci return NULL; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* 105262306a36Sopenharmony_ci * take reference for what will be the newly allocated hostport if 105362306a36Sopenharmony_ci * we end up using a new allocation 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_ci if (!nvmet_fc_tgtport_get(tgtport)) 105662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 105962306a36Sopenharmony_ci match = nvmet_fc_match_hostport(tgtport, hosthandle); 106062306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci if (match) { 106362306a36Sopenharmony_ci /* no new allocation - release reference */ 106462306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 106562306a36Sopenharmony_ci return match; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci newhost = kzalloc(sizeof(*newhost), GFP_KERNEL); 106962306a36Sopenharmony_ci if (!newhost) { 107062306a36Sopenharmony_ci /* no new allocation - release reference */ 107162306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 107262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 107662306a36Sopenharmony_ci match = nvmet_fc_match_hostport(tgtport, hosthandle); 107762306a36Sopenharmony_ci if (match) { 107862306a36Sopenharmony_ci /* new allocation not needed */ 107962306a36Sopenharmony_ci kfree(newhost); 108062306a36Sopenharmony_ci newhost = match; 108162306a36Sopenharmony_ci } else { 108262306a36Sopenharmony_ci newhost->tgtport = tgtport; 108362306a36Sopenharmony_ci newhost->hosthandle = hosthandle; 108462306a36Sopenharmony_ci INIT_LIST_HEAD(&newhost->host_list); 108562306a36Sopenharmony_ci kref_init(&newhost->ref); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci list_add_tail(&newhost->host_list, &tgtport->host_list); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return newhost; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic void 109562306a36Sopenharmony_cinvmet_fc_delete_assoc(struct nvmet_fc_tgt_assoc *assoc) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci nvmet_fc_delete_target_assoc(assoc); 109862306a36Sopenharmony_ci nvmet_fc_tgt_a_put(assoc); 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic void 110262306a36Sopenharmony_cinvmet_fc_delete_assoc_work(struct work_struct *work) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc = 110562306a36Sopenharmony_ci container_of(work, struct nvmet_fc_tgt_assoc, del_work); 110662306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = assoc->tgtport; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci nvmet_fc_delete_assoc(assoc); 110962306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic void 111362306a36Sopenharmony_cinvmet_fc_schedule_delete_assoc(struct nvmet_fc_tgt_assoc *assoc) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci nvmet_fc_tgtport_get(assoc->tgtport); 111662306a36Sopenharmony_ci queue_work(nvmet_wq, &assoc->del_work); 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_cistatic struct nvmet_fc_tgt_assoc * 112062306a36Sopenharmony_cinvmet_fc_alloc_target_assoc(struct nvmet_fc_tgtport *tgtport, void *hosthandle) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc, *tmpassoc; 112362306a36Sopenharmony_ci unsigned long flags; 112462306a36Sopenharmony_ci u64 ran; 112562306a36Sopenharmony_ci int idx; 112662306a36Sopenharmony_ci bool needrandom = true; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (!tgtport->pe) 112962306a36Sopenharmony_ci return NULL; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci assoc = kzalloc(sizeof(*assoc), GFP_KERNEL); 113262306a36Sopenharmony_ci if (!assoc) 113362306a36Sopenharmony_ci return NULL; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci idx = ida_alloc(&tgtport->assoc_cnt, GFP_KERNEL); 113662306a36Sopenharmony_ci if (idx < 0) 113762306a36Sopenharmony_ci goto out_free_assoc; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (!nvmet_fc_tgtport_get(tgtport)) 114062306a36Sopenharmony_ci goto out_ida; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci assoc->hostport = nvmet_fc_alloc_hostport(tgtport, hosthandle); 114362306a36Sopenharmony_ci if (IS_ERR(assoc->hostport)) 114462306a36Sopenharmony_ci goto out_put; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci assoc->tgtport = tgtport; 114762306a36Sopenharmony_ci assoc->a_id = idx; 114862306a36Sopenharmony_ci INIT_LIST_HEAD(&assoc->a_list); 114962306a36Sopenharmony_ci kref_init(&assoc->ref); 115062306a36Sopenharmony_ci INIT_WORK(&assoc->del_work, nvmet_fc_delete_assoc_work); 115162306a36Sopenharmony_ci atomic_set(&assoc->terminating, 0); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci while (needrandom) { 115462306a36Sopenharmony_ci get_random_bytes(&ran, sizeof(ran) - BYTES_FOR_QID); 115562306a36Sopenharmony_ci ran = ran << BYTES_FOR_QID_SHIFT; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 115862306a36Sopenharmony_ci needrandom = false; 115962306a36Sopenharmony_ci list_for_each_entry(tmpassoc, &tgtport->assoc_list, a_list) { 116062306a36Sopenharmony_ci if (ran == tmpassoc->association_id) { 116162306a36Sopenharmony_ci needrandom = true; 116262306a36Sopenharmony_ci break; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci if (!needrandom) { 116662306a36Sopenharmony_ci assoc->association_id = ran; 116762306a36Sopenharmony_ci list_add_tail_rcu(&assoc->a_list, &tgtport->assoc_list); 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci return assoc; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ciout_put: 117562306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 117662306a36Sopenharmony_ciout_ida: 117762306a36Sopenharmony_ci ida_free(&tgtport->assoc_cnt, idx); 117862306a36Sopenharmony_ciout_free_assoc: 117962306a36Sopenharmony_ci kfree(assoc); 118062306a36Sopenharmony_ci return NULL; 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic void 118462306a36Sopenharmony_cinvmet_fc_target_assoc_free(struct kref *ref) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc = 118762306a36Sopenharmony_ci container_of(ref, struct nvmet_fc_tgt_assoc, ref); 118862306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = assoc->tgtport; 118962306a36Sopenharmony_ci struct nvmet_fc_ls_iod *oldls; 119062306a36Sopenharmony_ci unsigned long flags; 119162306a36Sopenharmony_ci int i; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci for (i = NVMET_NR_QUEUES; i >= 0; i--) { 119462306a36Sopenharmony_ci if (assoc->queues[i]) 119562306a36Sopenharmony_ci nvmet_fc_delete_target_queue(assoc->queues[i]); 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* Send Disconnect now that all i/o has completed */ 119962306a36Sopenharmony_ci nvmet_fc_xmt_disconnect_assoc(assoc); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci nvmet_fc_free_hostport(assoc->hostport); 120262306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 120362306a36Sopenharmony_ci oldls = assoc->rcv_disconn; 120462306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 120562306a36Sopenharmony_ci /* if pending Rcv Disconnect Association LS, send rsp now */ 120662306a36Sopenharmony_ci if (oldls) 120762306a36Sopenharmony_ci nvmet_fc_xmt_ls_rsp(tgtport, oldls); 120862306a36Sopenharmony_ci ida_free(&tgtport->assoc_cnt, assoc->a_id); 120962306a36Sopenharmony_ci dev_info(tgtport->dev, 121062306a36Sopenharmony_ci "{%d:%d} Association freed\n", 121162306a36Sopenharmony_ci tgtport->fc_target_port.port_num, assoc->a_id); 121262306a36Sopenharmony_ci kfree_rcu(assoc, rcu); 121362306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic void 121762306a36Sopenharmony_cinvmet_fc_tgt_a_put(struct nvmet_fc_tgt_assoc *assoc) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci kref_put(&assoc->ref, nvmet_fc_target_assoc_free); 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic int 122362306a36Sopenharmony_cinvmet_fc_tgt_a_get(struct nvmet_fc_tgt_assoc *assoc) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci return kref_get_unless_zero(&assoc->ref); 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_cistatic void 122962306a36Sopenharmony_cinvmet_fc_delete_target_assoc(struct nvmet_fc_tgt_assoc *assoc) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = assoc->tgtport; 123262306a36Sopenharmony_ci unsigned long flags; 123362306a36Sopenharmony_ci int i, terminating; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci terminating = atomic_xchg(&assoc->terminating, 1); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* if already terminating, do nothing */ 123862306a36Sopenharmony_ci if (terminating) 123962306a36Sopenharmony_ci return; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 124262306a36Sopenharmony_ci list_del_rcu(&assoc->a_list); 124362306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci synchronize_rcu(); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* ensure all in-flight I/Os have been processed */ 124862306a36Sopenharmony_ci for (i = NVMET_NR_QUEUES; i >= 0; i--) { 124962306a36Sopenharmony_ci if (assoc->queues[i]) 125062306a36Sopenharmony_ci flush_workqueue(assoc->queues[i]->work_q); 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci dev_info(tgtport->dev, 125462306a36Sopenharmony_ci "{%d:%d} Association deleted\n", 125562306a36Sopenharmony_ci tgtport->fc_target_port.port_num, assoc->a_id); 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic struct nvmet_fc_tgt_assoc * 125962306a36Sopenharmony_cinvmet_fc_find_target_assoc(struct nvmet_fc_tgtport *tgtport, 126062306a36Sopenharmony_ci u64 association_id) 126162306a36Sopenharmony_ci{ 126262306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc; 126362306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *ret = NULL; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci rcu_read_lock(); 126662306a36Sopenharmony_ci list_for_each_entry_rcu(assoc, &tgtport->assoc_list, a_list) { 126762306a36Sopenharmony_ci if (association_id == assoc->association_id) { 126862306a36Sopenharmony_ci ret = assoc; 126962306a36Sopenharmony_ci if (!nvmet_fc_tgt_a_get(assoc)) 127062306a36Sopenharmony_ci ret = NULL; 127162306a36Sopenharmony_ci break; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci rcu_read_unlock(); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci return ret; 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic void 128062306a36Sopenharmony_cinvmet_fc_portentry_bind(struct nvmet_fc_tgtport *tgtport, 128162306a36Sopenharmony_ci struct nvmet_fc_port_entry *pe, 128262306a36Sopenharmony_ci struct nvmet_port *port) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci lockdep_assert_held(&nvmet_fc_tgtlock); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci pe->tgtport = tgtport; 128762306a36Sopenharmony_ci tgtport->pe = pe; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci pe->port = port; 129062306a36Sopenharmony_ci port->priv = pe; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci pe->node_name = tgtport->fc_target_port.node_name; 129362306a36Sopenharmony_ci pe->port_name = tgtport->fc_target_port.port_name; 129462306a36Sopenharmony_ci INIT_LIST_HEAD(&pe->pe_list); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci list_add_tail(&pe->pe_list, &nvmet_fc_portentry_list); 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic void 130062306a36Sopenharmony_cinvmet_fc_portentry_unbind(struct nvmet_fc_port_entry *pe) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci unsigned long flags; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci spin_lock_irqsave(&nvmet_fc_tgtlock, flags); 130562306a36Sopenharmony_ci if (pe->tgtport) 130662306a36Sopenharmony_ci pe->tgtport->pe = NULL; 130762306a36Sopenharmony_ci list_del(&pe->pe_list); 130862306a36Sopenharmony_ci spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags); 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci/* 131262306a36Sopenharmony_ci * called when a targetport deregisters. Breaks the relationship 131362306a36Sopenharmony_ci * with the nvmet port, but leaves the port_entry in place so that 131462306a36Sopenharmony_ci * re-registration can resume operation. 131562306a36Sopenharmony_ci */ 131662306a36Sopenharmony_cistatic void 131762306a36Sopenharmony_cinvmet_fc_portentry_unbind_tgt(struct nvmet_fc_tgtport *tgtport) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci struct nvmet_fc_port_entry *pe; 132062306a36Sopenharmony_ci unsigned long flags; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci spin_lock_irqsave(&nvmet_fc_tgtlock, flags); 132362306a36Sopenharmony_ci pe = tgtport->pe; 132462306a36Sopenharmony_ci if (pe) 132562306a36Sopenharmony_ci pe->tgtport = NULL; 132662306a36Sopenharmony_ci tgtport->pe = NULL; 132762306a36Sopenharmony_ci spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags); 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci/* 133162306a36Sopenharmony_ci * called when a new targetport is registered. Looks in the 133262306a36Sopenharmony_ci * existing nvmet port_entries to see if the nvmet layer is 133362306a36Sopenharmony_ci * configured for the targetport's wwn's. (the targetport existed, 133462306a36Sopenharmony_ci * nvmet configured, the lldd unregistered the tgtport, and is now 133562306a36Sopenharmony_ci * reregistering the same targetport). If so, set the nvmet port 133662306a36Sopenharmony_ci * port entry on the targetport. 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_cistatic void 133962306a36Sopenharmony_cinvmet_fc_portentry_rebind_tgt(struct nvmet_fc_tgtport *tgtport) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci struct nvmet_fc_port_entry *pe; 134262306a36Sopenharmony_ci unsigned long flags; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci spin_lock_irqsave(&nvmet_fc_tgtlock, flags); 134562306a36Sopenharmony_ci list_for_each_entry(pe, &nvmet_fc_portentry_list, pe_list) { 134662306a36Sopenharmony_ci if (tgtport->fc_target_port.node_name == pe->node_name && 134762306a36Sopenharmony_ci tgtport->fc_target_port.port_name == pe->port_name) { 134862306a36Sopenharmony_ci WARN_ON(pe->tgtport); 134962306a36Sopenharmony_ci tgtport->pe = pe; 135062306a36Sopenharmony_ci pe->tgtport = tgtport; 135162306a36Sopenharmony_ci break; 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags); 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci/** 135862306a36Sopenharmony_ci * nvmet_fc_register_targetport - transport entry point called by an 135962306a36Sopenharmony_ci * LLDD to register the existence of a local 136062306a36Sopenharmony_ci * NVME subystem FC port. 136162306a36Sopenharmony_ci * @pinfo: pointer to information about the port to be registered 136262306a36Sopenharmony_ci * @template: LLDD entrypoints and operational parameters for the port 136362306a36Sopenharmony_ci * @dev: physical hardware device node port corresponds to. Will be 136462306a36Sopenharmony_ci * used for DMA mappings 136562306a36Sopenharmony_ci * @portptr: pointer to a local port pointer. Upon success, the routine 136662306a36Sopenharmony_ci * will allocate a nvme_fc_local_port structure and place its 136762306a36Sopenharmony_ci * address in the local port pointer. Upon failure, local port 136862306a36Sopenharmony_ci * pointer will be set to NULL. 136962306a36Sopenharmony_ci * 137062306a36Sopenharmony_ci * Returns: 137162306a36Sopenharmony_ci * a completion status. Must be 0 upon success; a negative errno 137262306a36Sopenharmony_ci * (ex: -ENXIO) upon failure. 137362306a36Sopenharmony_ci */ 137462306a36Sopenharmony_ciint 137562306a36Sopenharmony_cinvmet_fc_register_targetport(struct nvmet_fc_port_info *pinfo, 137662306a36Sopenharmony_ci struct nvmet_fc_target_template *template, 137762306a36Sopenharmony_ci struct device *dev, 137862306a36Sopenharmony_ci struct nvmet_fc_target_port **portptr) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct nvmet_fc_tgtport *newrec; 138162306a36Sopenharmony_ci unsigned long flags; 138262306a36Sopenharmony_ci int ret, idx; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci if (!template->xmt_ls_rsp || !template->fcp_op || 138562306a36Sopenharmony_ci !template->fcp_abort || 138662306a36Sopenharmony_ci !template->fcp_req_release || !template->targetport_delete || 138762306a36Sopenharmony_ci !template->max_hw_queues || !template->max_sgl_segments || 138862306a36Sopenharmony_ci !template->max_dif_sgl_segments || !template->dma_boundary) { 138962306a36Sopenharmony_ci ret = -EINVAL; 139062306a36Sopenharmony_ci goto out_regtgt_failed; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci newrec = kzalloc((sizeof(*newrec) + template->target_priv_sz), 139462306a36Sopenharmony_ci GFP_KERNEL); 139562306a36Sopenharmony_ci if (!newrec) { 139662306a36Sopenharmony_ci ret = -ENOMEM; 139762306a36Sopenharmony_ci goto out_regtgt_failed; 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci idx = ida_alloc(&nvmet_fc_tgtport_cnt, GFP_KERNEL); 140162306a36Sopenharmony_ci if (idx < 0) { 140262306a36Sopenharmony_ci ret = -ENOSPC; 140362306a36Sopenharmony_ci goto out_fail_kfree; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (!get_device(dev) && dev) { 140762306a36Sopenharmony_ci ret = -ENODEV; 140862306a36Sopenharmony_ci goto out_ida_put; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci newrec->fc_target_port.node_name = pinfo->node_name; 141262306a36Sopenharmony_ci newrec->fc_target_port.port_name = pinfo->port_name; 141362306a36Sopenharmony_ci if (template->target_priv_sz) 141462306a36Sopenharmony_ci newrec->fc_target_port.private = &newrec[1]; 141562306a36Sopenharmony_ci else 141662306a36Sopenharmony_ci newrec->fc_target_port.private = NULL; 141762306a36Sopenharmony_ci newrec->fc_target_port.port_id = pinfo->port_id; 141862306a36Sopenharmony_ci newrec->fc_target_port.port_num = idx; 141962306a36Sopenharmony_ci INIT_LIST_HEAD(&newrec->tgt_list); 142062306a36Sopenharmony_ci newrec->dev = dev; 142162306a36Sopenharmony_ci newrec->ops = template; 142262306a36Sopenharmony_ci spin_lock_init(&newrec->lock); 142362306a36Sopenharmony_ci INIT_LIST_HEAD(&newrec->ls_rcv_list); 142462306a36Sopenharmony_ci INIT_LIST_HEAD(&newrec->ls_req_list); 142562306a36Sopenharmony_ci INIT_LIST_HEAD(&newrec->ls_busylist); 142662306a36Sopenharmony_ci INIT_LIST_HEAD(&newrec->assoc_list); 142762306a36Sopenharmony_ci INIT_LIST_HEAD(&newrec->host_list); 142862306a36Sopenharmony_ci kref_init(&newrec->ref); 142962306a36Sopenharmony_ci ida_init(&newrec->assoc_cnt); 143062306a36Sopenharmony_ci newrec->max_sg_cnt = template->max_sgl_segments; 143162306a36Sopenharmony_ci INIT_WORK(&newrec->put_work, nvmet_fc_put_tgtport_work); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci ret = nvmet_fc_alloc_ls_iodlist(newrec); 143462306a36Sopenharmony_ci if (ret) { 143562306a36Sopenharmony_ci ret = -ENOMEM; 143662306a36Sopenharmony_ci goto out_free_newrec; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci nvmet_fc_portentry_rebind_tgt(newrec); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci spin_lock_irqsave(&nvmet_fc_tgtlock, flags); 144262306a36Sopenharmony_ci list_add_tail(&newrec->tgt_list, &nvmet_fc_target_list); 144362306a36Sopenharmony_ci spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci *portptr = &newrec->fc_target_port; 144662306a36Sopenharmony_ci return 0; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ciout_free_newrec: 144962306a36Sopenharmony_ci put_device(dev); 145062306a36Sopenharmony_ciout_ida_put: 145162306a36Sopenharmony_ci ida_free(&nvmet_fc_tgtport_cnt, idx); 145262306a36Sopenharmony_ciout_fail_kfree: 145362306a36Sopenharmony_ci kfree(newrec); 145462306a36Sopenharmony_ciout_regtgt_failed: 145562306a36Sopenharmony_ci *portptr = NULL; 145662306a36Sopenharmony_ci return ret; 145762306a36Sopenharmony_ci} 145862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmet_fc_register_targetport); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_cistatic void 146262306a36Sopenharmony_cinvmet_fc_free_tgtport(struct kref *ref) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = 146562306a36Sopenharmony_ci container_of(ref, struct nvmet_fc_tgtport, ref); 146662306a36Sopenharmony_ci struct device *dev = tgtport->dev; 146762306a36Sopenharmony_ci unsigned long flags; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci spin_lock_irqsave(&nvmet_fc_tgtlock, flags); 147062306a36Sopenharmony_ci list_del(&tgtport->tgt_list); 147162306a36Sopenharmony_ci spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci nvmet_fc_free_ls_iodlist(tgtport); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci /* let the LLDD know we've finished tearing it down */ 147662306a36Sopenharmony_ci tgtport->ops->targetport_delete(&tgtport->fc_target_port); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci ida_free(&nvmet_fc_tgtport_cnt, 147962306a36Sopenharmony_ci tgtport->fc_target_port.port_num); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci ida_destroy(&tgtport->assoc_cnt); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci kfree(tgtport); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci put_device(dev); 148662306a36Sopenharmony_ci} 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_cistatic void 148962306a36Sopenharmony_cinvmet_fc_tgtport_put(struct nvmet_fc_tgtport *tgtport) 149062306a36Sopenharmony_ci{ 149162306a36Sopenharmony_ci kref_put(&tgtport->ref, nvmet_fc_free_tgtport); 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic int 149562306a36Sopenharmony_cinvmet_fc_tgtport_get(struct nvmet_fc_tgtport *tgtport) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci return kref_get_unless_zero(&tgtport->ref); 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic void 150162306a36Sopenharmony_ci__nvmet_fc_free_assocs(struct nvmet_fc_tgtport *tgtport) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci rcu_read_lock(); 150662306a36Sopenharmony_ci list_for_each_entry_rcu(assoc, &tgtport->assoc_list, a_list) { 150762306a36Sopenharmony_ci if (!nvmet_fc_tgt_a_get(assoc)) 150862306a36Sopenharmony_ci continue; 150962306a36Sopenharmony_ci nvmet_fc_schedule_delete_assoc(assoc); 151062306a36Sopenharmony_ci nvmet_fc_tgt_a_put(assoc); 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci rcu_read_unlock(); 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci/** 151662306a36Sopenharmony_ci * nvmet_fc_invalidate_host - transport entry point called by an LLDD 151762306a36Sopenharmony_ci * to remove references to a hosthandle for LS's. 151862306a36Sopenharmony_ci * 151962306a36Sopenharmony_ci * The nvmet-fc layer ensures that any references to the hosthandle 152062306a36Sopenharmony_ci * on the targetport are forgotten (set to NULL). The LLDD will 152162306a36Sopenharmony_ci * typically call this when a login with a remote host port has been 152262306a36Sopenharmony_ci * lost, thus LS's for the remote host port are no longer possible. 152362306a36Sopenharmony_ci * 152462306a36Sopenharmony_ci * If an LS request is outstanding to the targetport/hosthandle (or 152562306a36Sopenharmony_ci * issued concurrently with the call to invalidate the host), the 152662306a36Sopenharmony_ci * LLDD is responsible for terminating/aborting the LS and completing 152762306a36Sopenharmony_ci * the LS request. It is recommended that these terminations/aborts 152862306a36Sopenharmony_ci * occur after calling to invalidate the host handle to avoid additional 152962306a36Sopenharmony_ci * retries by the nvmet-fc transport. The nvmet-fc transport may 153062306a36Sopenharmony_ci * continue to reference host handle while it cleans up outstanding 153162306a36Sopenharmony_ci * NVME associations. The nvmet-fc transport will call the 153262306a36Sopenharmony_ci * ops->host_release() callback to notify the LLDD that all references 153362306a36Sopenharmony_ci * are complete and the related host handle can be recovered. 153462306a36Sopenharmony_ci * Note: if there are no references, the callback may be called before 153562306a36Sopenharmony_ci * the invalidate host call returns. 153662306a36Sopenharmony_ci * 153762306a36Sopenharmony_ci * @target_port: pointer to the (registered) target port that a prior 153862306a36Sopenharmony_ci * LS was received on and which supplied the transport the 153962306a36Sopenharmony_ci * hosthandle. 154062306a36Sopenharmony_ci * @hosthandle: the handle (pointer) that represents the host port 154162306a36Sopenharmony_ci * that no longer has connectivity and that LS's should 154262306a36Sopenharmony_ci * no longer be directed to. 154362306a36Sopenharmony_ci */ 154462306a36Sopenharmony_civoid 154562306a36Sopenharmony_cinvmet_fc_invalidate_host(struct nvmet_fc_target_port *target_port, 154662306a36Sopenharmony_ci void *hosthandle) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = targetport_to_tgtport(target_port); 154962306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc, *next; 155062306a36Sopenharmony_ci unsigned long flags; 155162306a36Sopenharmony_ci bool noassoc = true; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 155462306a36Sopenharmony_ci list_for_each_entry_safe(assoc, next, 155562306a36Sopenharmony_ci &tgtport->assoc_list, a_list) { 155662306a36Sopenharmony_ci if (!assoc->hostport || 155762306a36Sopenharmony_ci assoc->hostport->hosthandle != hosthandle) 155862306a36Sopenharmony_ci continue; 155962306a36Sopenharmony_ci if (!nvmet_fc_tgt_a_get(assoc)) 156062306a36Sopenharmony_ci continue; 156162306a36Sopenharmony_ci assoc->hostport->invalid = 1; 156262306a36Sopenharmony_ci noassoc = false; 156362306a36Sopenharmony_ci nvmet_fc_schedule_delete_assoc(assoc); 156462306a36Sopenharmony_ci nvmet_fc_tgt_a_put(assoc); 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci /* if there's nothing to wait for - call the callback */ 156962306a36Sopenharmony_ci if (noassoc && tgtport->ops->host_release) 157062306a36Sopenharmony_ci tgtport->ops->host_release(hosthandle); 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmet_fc_invalidate_host); 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci/* 157562306a36Sopenharmony_ci * nvmet layer has called to terminate an association 157662306a36Sopenharmony_ci */ 157762306a36Sopenharmony_cistatic void 157862306a36Sopenharmony_cinvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl) 157962306a36Sopenharmony_ci{ 158062306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport, *next; 158162306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc; 158262306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue; 158362306a36Sopenharmony_ci unsigned long flags; 158462306a36Sopenharmony_ci bool found_ctrl = false; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci /* this is a bit ugly, but don't want to make locks layered */ 158762306a36Sopenharmony_ci spin_lock_irqsave(&nvmet_fc_tgtlock, flags); 158862306a36Sopenharmony_ci list_for_each_entry_safe(tgtport, next, &nvmet_fc_target_list, 158962306a36Sopenharmony_ci tgt_list) { 159062306a36Sopenharmony_ci if (!nvmet_fc_tgtport_get(tgtport)) 159162306a36Sopenharmony_ci continue; 159262306a36Sopenharmony_ci spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci rcu_read_lock(); 159562306a36Sopenharmony_ci list_for_each_entry_rcu(assoc, &tgtport->assoc_list, a_list) { 159662306a36Sopenharmony_ci queue = assoc->queues[0]; 159762306a36Sopenharmony_ci if (queue && queue->nvme_sq.ctrl == ctrl) { 159862306a36Sopenharmony_ci if (nvmet_fc_tgt_a_get(assoc)) 159962306a36Sopenharmony_ci found_ctrl = true; 160062306a36Sopenharmony_ci break; 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci rcu_read_unlock(); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci if (found_ctrl) { 160862306a36Sopenharmony_ci nvmet_fc_schedule_delete_assoc(assoc); 160962306a36Sopenharmony_ci nvmet_fc_tgt_a_put(assoc); 161062306a36Sopenharmony_ci return; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci spin_lock_irqsave(&nvmet_fc_tgtlock, flags); 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags); 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci/** 161962306a36Sopenharmony_ci * nvmet_fc_unregister_targetport - transport entry point called by an 162062306a36Sopenharmony_ci * LLDD to deregister/remove a previously 162162306a36Sopenharmony_ci * registered a local NVME subsystem FC port. 162262306a36Sopenharmony_ci * @target_port: pointer to the (registered) target port that is to be 162362306a36Sopenharmony_ci * deregistered. 162462306a36Sopenharmony_ci * 162562306a36Sopenharmony_ci * Returns: 162662306a36Sopenharmony_ci * a completion status. Must be 0 upon success; a negative errno 162762306a36Sopenharmony_ci * (ex: -ENXIO) upon failure. 162862306a36Sopenharmony_ci */ 162962306a36Sopenharmony_ciint 163062306a36Sopenharmony_cinvmet_fc_unregister_targetport(struct nvmet_fc_target_port *target_port) 163162306a36Sopenharmony_ci{ 163262306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = targetport_to_tgtport(target_port); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci nvmet_fc_portentry_unbind_tgt(tgtport); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci /* terminate any outstanding associations */ 163762306a36Sopenharmony_ci __nvmet_fc_free_assocs(tgtport); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci flush_workqueue(nvmet_wq); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci /* 164262306a36Sopenharmony_ci * should terminate LS's as well. However, LS's will be generated 164362306a36Sopenharmony_ci * at the tail end of association termination, so they likely don't 164462306a36Sopenharmony_ci * exist yet. And even if they did, it's worthwhile to just let 164562306a36Sopenharmony_ci * them finish and targetport ref counting will clean things up. 164662306a36Sopenharmony_ci */ 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci return 0; 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmet_fc_unregister_targetport); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci/* ********************** FC-NVME LS RCV Handling ************************* */ 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_cistatic void 165962306a36Sopenharmony_cinvmet_fc_ls_create_association(struct nvmet_fc_tgtport *tgtport, 166062306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci struct fcnvme_ls_cr_assoc_rqst *rqst = &iod->rqstbuf->rq_cr_assoc; 166362306a36Sopenharmony_ci struct fcnvme_ls_cr_assoc_acc *acc = &iod->rspbuf->rsp_cr_assoc; 166462306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue; 166562306a36Sopenharmony_ci int ret = 0; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci memset(acc, 0, sizeof(*acc)); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci /* 167062306a36Sopenharmony_ci * FC-NVME spec changes. There are initiators sending different 167162306a36Sopenharmony_ci * lengths as padding sizes for Create Association Cmd descriptor 167262306a36Sopenharmony_ci * was incorrect. 167362306a36Sopenharmony_ci * Accept anything of "minimum" length. Assume format per 1.15 167462306a36Sopenharmony_ci * spec (with HOSTID reduced to 16 bytes), ignore how long the 167562306a36Sopenharmony_ci * trailing pad length is. 167662306a36Sopenharmony_ci */ 167762306a36Sopenharmony_ci if (iod->rqstdatalen < FCNVME_LSDESC_CRA_RQST_MINLEN) 167862306a36Sopenharmony_ci ret = VERR_CR_ASSOC_LEN; 167962306a36Sopenharmony_ci else if (be32_to_cpu(rqst->desc_list_len) < 168062306a36Sopenharmony_ci FCNVME_LSDESC_CRA_RQST_MIN_LISTLEN) 168162306a36Sopenharmony_ci ret = VERR_CR_ASSOC_RQST_LEN; 168262306a36Sopenharmony_ci else if (rqst->assoc_cmd.desc_tag != 168362306a36Sopenharmony_ci cpu_to_be32(FCNVME_LSDESC_CREATE_ASSOC_CMD)) 168462306a36Sopenharmony_ci ret = VERR_CR_ASSOC_CMD; 168562306a36Sopenharmony_ci else if (be32_to_cpu(rqst->assoc_cmd.desc_len) < 168662306a36Sopenharmony_ci FCNVME_LSDESC_CRA_CMD_DESC_MIN_DESCLEN) 168762306a36Sopenharmony_ci ret = VERR_CR_ASSOC_CMD_LEN; 168862306a36Sopenharmony_ci else if (!rqst->assoc_cmd.ersp_ratio || 168962306a36Sopenharmony_ci (be16_to_cpu(rqst->assoc_cmd.ersp_ratio) >= 169062306a36Sopenharmony_ci be16_to_cpu(rqst->assoc_cmd.sqsize))) 169162306a36Sopenharmony_ci ret = VERR_ERSP_RATIO; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci else { 169462306a36Sopenharmony_ci /* new association w/ admin queue */ 169562306a36Sopenharmony_ci iod->assoc = nvmet_fc_alloc_target_assoc( 169662306a36Sopenharmony_ci tgtport, iod->hosthandle); 169762306a36Sopenharmony_ci if (!iod->assoc) 169862306a36Sopenharmony_ci ret = VERR_ASSOC_ALLOC_FAIL; 169962306a36Sopenharmony_ci else { 170062306a36Sopenharmony_ci queue = nvmet_fc_alloc_target_queue(iod->assoc, 0, 170162306a36Sopenharmony_ci be16_to_cpu(rqst->assoc_cmd.sqsize)); 170262306a36Sopenharmony_ci if (!queue) { 170362306a36Sopenharmony_ci ret = VERR_QUEUE_ALLOC_FAIL; 170462306a36Sopenharmony_ci nvmet_fc_tgt_a_put(iod->assoc); 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci if (ret) { 171062306a36Sopenharmony_ci dev_err(tgtport->dev, 171162306a36Sopenharmony_ci "Create Association LS failed: %s\n", 171262306a36Sopenharmony_ci validation_errors[ret]); 171362306a36Sopenharmony_ci iod->lsrsp->rsplen = nvme_fc_format_rjt(acc, 171462306a36Sopenharmony_ci sizeof(*acc), rqst->w0.ls_cmd, 171562306a36Sopenharmony_ci FCNVME_RJT_RC_LOGIC, 171662306a36Sopenharmony_ci FCNVME_RJT_EXP_NONE, 0); 171762306a36Sopenharmony_ci return; 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci queue->ersp_ratio = be16_to_cpu(rqst->assoc_cmd.ersp_ratio); 172162306a36Sopenharmony_ci atomic_set(&queue->connected, 1); 172262306a36Sopenharmony_ci queue->sqhd = 0; /* best place to init value */ 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci dev_info(tgtport->dev, 172562306a36Sopenharmony_ci "{%d:%d} Association created\n", 172662306a36Sopenharmony_ci tgtport->fc_target_port.port_num, iod->assoc->a_id); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci /* format a response */ 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci iod->lsrsp->rsplen = sizeof(*acc); 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci nvme_fc_format_rsp_hdr(acc, FCNVME_LS_ACC, 173362306a36Sopenharmony_ci fcnvme_lsdesc_len( 173462306a36Sopenharmony_ci sizeof(struct fcnvme_ls_cr_assoc_acc)), 173562306a36Sopenharmony_ci FCNVME_LS_CREATE_ASSOCIATION); 173662306a36Sopenharmony_ci acc->associd.desc_tag = cpu_to_be32(FCNVME_LSDESC_ASSOC_ID); 173762306a36Sopenharmony_ci acc->associd.desc_len = 173862306a36Sopenharmony_ci fcnvme_lsdesc_len( 173962306a36Sopenharmony_ci sizeof(struct fcnvme_lsdesc_assoc_id)); 174062306a36Sopenharmony_ci acc->associd.association_id = 174162306a36Sopenharmony_ci cpu_to_be64(nvmet_fc_makeconnid(iod->assoc, 0)); 174262306a36Sopenharmony_ci acc->connectid.desc_tag = cpu_to_be32(FCNVME_LSDESC_CONN_ID); 174362306a36Sopenharmony_ci acc->connectid.desc_len = 174462306a36Sopenharmony_ci fcnvme_lsdesc_len( 174562306a36Sopenharmony_ci sizeof(struct fcnvme_lsdesc_conn_id)); 174662306a36Sopenharmony_ci acc->connectid.connection_id = acc->associd.association_id; 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_cistatic void 175062306a36Sopenharmony_cinvmet_fc_ls_create_connection(struct nvmet_fc_tgtport *tgtport, 175162306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod) 175262306a36Sopenharmony_ci{ 175362306a36Sopenharmony_ci struct fcnvme_ls_cr_conn_rqst *rqst = &iod->rqstbuf->rq_cr_conn; 175462306a36Sopenharmony_ci struct fcnvme_ls_cr_conn_acc *acc = &iod->rspbuf->rsp_cr_conn; 175562306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue; 175662306a36Sopenharmony_ci int ret = 0; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci memset(acc, 0, sizeof(*acc)); 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci if (iod->rqstdatalen < sizeof(struct fcnvme_ls_cr_conn_rqst)) 176162306a36Sopenharmony_ci ret = VERR_CR_CONN_LEN; 176262306a36Sopenharmony_ci else if (rqst->desc_list_len != 176362306a36Sopenharmony_ci fcnvme_lsdesc_len( 176462306a36Sopenharmony_ci sizeof(struct fcnvme_ls_cr_conn_rqst))) 176562306a36Sopenharmony_ci ret = VERR_CR_CONN_RQST_LEN; 176662306a36Sopenharmony_ci else if (rqst->associd.desc_tag != cpu_to_be32(FCNVME_LSDESC_ASSOC_ID)) 176762306a36Sopenharmony_ci ret = VERR_ASSOC_ID; 176862306a36Sopenharmony_ci else if (rqst->associd.desc_len != 176962306a36Sopenharmony_ci fcnvme_lsdesc_len( 177062306a36Sopenharmony_ci sizeof(struct fcnvme_lsdesc_assoc_id))) 177162306a36Sopenharmony_ci ret = VERR_ASSOC_ID_LEN; 177262306a36Sopenharmony_ci else if (rqst->connect_cmd.desc_tag != 177362306a36Sopenharmony_ci cpu_to_be32(FCNVME_LSDESC_CREATE_CONN_CMD)) 177462306a36Sopenharmony_ci ret = VERR_CR_CONN_CMD; 177562306a36Sopenharmony_ci else if (rqst->connect_cmd.desc_len != 177662306a36Sopenharmony_ci fcnvme_lsdesc_len( 177762306a36Sopenharmony_ci sizeof(struct fcnvme_lsdesc_cr_conn_cmd))) 177862306a36Sopenharmony_ci ret = VERR_CR_CONN_CMD_LEN; 177962306a36Sopenharmony_ci else if (!rqst->connect_cmd.ersp_ratio || 178062306a36Sopenharmony_ci (be16_to_cpu(rqst->connect_cmd.ersp_ratio) >= 178162306a36Sopenharmony_ci be16_to_cpu(rqst->connect_cmd.sqsize))) 178262306a36Sopenharmony_ci ret = VERR_ERSP_RATIO; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci else { 178562306a36Sopenharmony_ci /* new io queue */ 178662306a36Sopenharmony_ci iod->assoc = nvmet_fc_find_target_assoc(tgtport, 178762306a36Sopenharmony_ci be64_to_cpu(rqst->associd.association_id)); 178862306a36Sopenharmony_ci if (!iod->assoc) 178962306a36Sopenharmony_ci ret = VERR_NO_ASSOC; 179062306a36Sopenharmony_ci else { 179162306a36Sopenharmony_ci queue = nvmet_fc_alloc_target_queue(iod->assoc, 179262306a36Sopenharmony_ci be16_to_cpu(rqst->connect_cmd.qid), 179362306a36Sopenharmony_ci be16_to_cpu(rqst->connect_cmd.sqsize)); 179462306a36Sopenharmony_ci if (!queue) 179562306a36Sopenharmony_ci ret = VERR_QUEUE_ALLOC_FAIL; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci /* release get taken in nvmet_fc_find_target_assoc */ 179862306a36Sopenharmony_ci nvmet_fc_tgt_a_put(iod->assoc); 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci if (ret) { 180362306a36Sopenharmony_ci dev_err(tgtport->dev, 180462306a36Sopenharmony_ci "Create Connection LS failed: %s\n", 180562306a36Sopenharmony_ci validation_errors[ret]); 180662306a36Sopenharmony_ci iod->lsrsp->rsplen = nvme_fc_format_rjt(acc, 180762306a36Sopenharmony_ci sizeof(*acc), rqst->w0.ls_cmd, 180862306a36Sopenharmony_ci (ret == VERR_NO_ASSOC) ? 180962306a36Sopenharmony_ci FCNVME_RJT_RC_INV_ASSOC : 181062306a36Sopenharmony_ci FCNVME_RJT_RC_LOGIC, 181162306a36Sopenharmony_ci FCNVME_RJT_EXP_NONE, 0); 181262306a36Sopenharmony_ci return; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci queue->ersp_ratio = be16_to_cpu(rqst->connect_cmd.ersp_ratio); 181662306a36Sopenharmony_ci atomic_set(&queue->connected, 1); 181762306a36Sopenharmony_ci queue->sqhd = 0; /* best place to init value */ 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci /* format a response */ 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci iod->lsrsp->rsplen = sizeof(*acc); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci nvme_fc_format_rsp_hdr(acc, FCNVME_LS_ACC, 182462306a36Sopenharmony_ci fcnvme_lsdesc_len(sizeof(struct fcnvme_ls_cr_conn_acc)), 182562306a36Sopenharmony_ci FCNVME_LS_CREATE_CONNECTION); 182662306a36Sopenharmony_ci acc->connectid.desc_tag = cpu_to_be32(FCNVME_LSDESC_CONN_ID); 182762306a36Sopenharmony_ci acc->connectid.desc_len = 182862306a36Sopenharmony_ci fcnvme_lsdesc_len( 182962306a36Sopenharmony_ci sizeof(struct fcnvme_lsdesc_conn_id)); 183062306a36Sopenharmony_ci acc->connectid.connection_id = 183162306a36Sopenharmony_ci cpu_to_be64(nvmet_fc_makeconnid(iod->assoc, 183262306a36Sopenharmony_ci be16_to_cpu(rqst->connect_cmd.qid))); 183362306a36Sopenharmony_ci} 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci/* 183662306a36Sopenharmony_ci * Returns true if the LS response is to be transmit 183762306a36Sopenharmony_ci * Returns false if the LS response is to be delayed 183862306a36Sopenharmony_ci */ 183962306a36Sopenharmony_cistatic int 184062306a36Sopenharmony_cinvmet_fc_ls_disconnect(struct nvmet_fc_tgtport *tgtport, 184162306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci struct fcnvme_ls_disconnect_assoc_rqst *rqst = 184462306a36Sopenharmony_ci &iod->rqstbuf->rq_dis_assoc; 184562306a36Sopenharmony_ci struct fcnvme_ls_disconnect_assoc_acc *acc = 184662306a36Sopenharmony_ci &iod->rspbuf->rsp_dis_assoc; 184762306a36Sopenharmony_ci struct nvmet_fc_tgt_assoc *assoc = NULL; 184862306a36Sopenharmony_ci struct nvmet_fc_ls_iod *oldls = NULL; 184962306a36Sopenharmony_ci unsigned long flags; 185062306a36Sopenharmony_ci int ret = 0; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci memset(acc, 0, sizeof(*acc)); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci ret = nvmefc_vldt_lsreq_discon_assoc(iod->rqstdatalen, rqst); 185562306a36Sopenharmony_ci if (!ret) { 185662306a36Sopenharmony_ci /* match an active association - takes an assoc ref if !NULL */ 185762306a36Sopenharmony_ci assoc = nvmet_fc_find_target_assoc(tgtport, 185862306a36Sopenharmony_ci be64_to_cpu(rqst->associd.association_id)); 185962306a36Sopenharmony_ci iod->assoc = assoc; 186062306a36Sopenharmony_ci if (!assoc) 186162306a36Sopenharmony_ci ret = VERR_NO_ASSOC; 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci if (ret || !assoc) { 186562306a36Sopenharmony_ci dev_err(tgtport->dev, 186662306a36Sopenharmony_ci "Disconnect LS failed: %s\n", 186762306a36Sopenharmony_ci validation_errors[ret]); 186862306a36Sopenharmony_ci iod->lsrsp->rsplen = nvme_fc_format_rjt(acc, 186962306a36Sopenharmony_ci sizeof(*acc), rqst->w0.ls_cmd, 187062306a36Sopenharmony_ci (ret == VERR_NO_ASSOC) ? 187162306a36Sopenharmony_ci FCNVME_RJT_RC_INV_ASSOC : 187262306a36Sopenharmony_ci FCNVME_RJT_RC_LOGIC, 187362306a36Sopenharmony_ci FCNVME_RJT_EXP_NONE, 0); 187462306a36Sopenharmony_ci return true; 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci /* format a response */ 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci iod->lsrsp->rsplen = sizeof(*acc); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci nvme_fc_format_rsp_hdr(acc, FCNVME_LS_ACC, 188262306a36Sopenharmony_ci fcnvme_lsdesc_len( 188362306a36Sopenharmony_ci sizeof(struct fcnvme_ls_disconnect_assoc_acc)), 188462306a36Sopenharmony_ci FCNVME_LS_DISCONNECT_ASSOC); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci /* 188762306a36Sopenharmony_ci * The rules for LS response says the response cannot 188862306a36Sopenharmony_ci * go back until ABTS's have been sent for all outstanding 188962306a36Sopenharmony_ci * I/O and a Disconnect Association LS has been sent. 189062306a36Sopenharmony_ci * So... save off the Disconnect LS to send the response 189162306a36Sopenharmony_ci * later. If there was a prior LS already saved, replace 189262306a36Sopenharmony_ci * it with the newer one and send a can't perform reject 189362306a36Sopenharmony_ci * on the older one. 189462306a36Sopenharmony_ci */ 189562306a36Sopenharmony_ci spin_lock_irqsave(&tgtport->lock, flags); 189662306a36Sopenharmony_ci oldls = assoc->rcv_disconn; 189762306a36Sopenharmony_ci assoc->rcv_disconn = iod; 189862306a36Sopenharmony_ci spin_unlock_irqrestore(&tgtport->lock, flags); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci if (oldls) { 190162306a36Sopenharmony_ci dev_info(tgtport->dev, 190262306a36Sopenharmony_ci "{%d:%d} Multiple Disconnect Association LS's " 190362306a36Sopenharmony_ci "received\n", 190462306a36Sopenharmony_ci tgtport->fc_target_port.port_num, assoc->a_id); 190562306a36Sopenharmony_ci /* overwrite good response with bogus failure */ 190662306a36Sopenharmony_ci oldls->lsrsp->rsplen = nvme_fc_format_rjt(oldls->rspbuf, 190762306a36Sopenharmony_ci sizeof(*iod->rspbuf), 190862306a36Sopenharmony_ci /* ok to use rqst, LS is same */ 190962306a36Sopenharmony_ci rqst->w0.ls_cmd, 191062306a36Sopenharmony_ci FCNVME_RJT_RC_UNAB, 191162306a36Sopenharmony_ci FCNVME_RJT_EXP_NONE, 0); 191262306a36Sopenharmony_ci nvmet_fc_xmt_ls_rsp(tgtport, oldls); 191362306a36Sopenharmony_ci } 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci nvmet_fc_schedule_delete_assoc(assoc); 191662306a36Sopenharmony_ci nvmet_fc_tgt_a_put(assoc); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci return false; 191962306a36Sopenharmony_ci} 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci/* *********************** NVME Ctrl Routines **************************** */ 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_cistatic void nvmet_fc_fcp_nvme_cmd_done(struct nvmet_req *nvme_req); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_cistatic const struct nvmet_fabrics_ops nvmet_fc_tgt_fcp_ops; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_cistatic void 193062306a36Sopenharmony_cinvmet_fc_xmt_ls_rsp_done(struct nvmefc_ls_rsp *lsrsp) 193162306a36Sopenharmony_ci{ 193262306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod = lsrsp->nvme_fc_private; 193362306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = iod->tgtport; 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci fc_dma_sync_single_for_cpu(tgtport->dev, iod->rspdma, 193662306a36Sopenharmony_ci sizeof(*iod->rspbuf), DMA_TO_DEVICE); 193762306a36Sopenharmony_ci nvmet_fc_free_ls_iod(tgtport, iod); 193862306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 193962306a36Sopenharmony_ci} 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_cistatic void 194262306a36Sopenharmony_cinvmet_fc_xmt_ls_rsp(struct nvmet_fc_tgtport *tgtport, 194362306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci int ret; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci fc_dma_sync_single_for_device(tgtport->dev, iod->rspdma, 194862306a36Sopenharmony_ci sizeof(*iod->rspbuf), DMA_TO_DEVICE); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci ret = tgtport->ops->xmt_ls_rsp(&tgtport->fc_target_port, iod->lsrsp); 195162306a36Sopenharmony_ci if (ret) 195262306a36Sopenharmony_ci nvmet_fc_xmt_ls_rsp_done(iod->lsrsp); 195362306a36Sopenharmony_ci} 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci/* 195662306a36Sopenharmony_ci * Actual processing routine for received FC-NVME LS Requests from the LLD 195762306a36Sopenharmony_ci */ 195862306a36Sopenharmony_cistatic void 195962306a36Sopenharmony_cinvmet_fc_handle_ls_rqst(struct nvmet_fc_tgtport *tgtport, 196062306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod) 196162306a36Sopenharmony_ci{ 196262306a36Sopenharmony_ci struct fcnvme_ls_rqst_w0 *w0 = &iod->rqstbuf->rq_cr_assoc.w0; 196362306a36Sopenharmony_ci bool sendrsp = true; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci iod->lsrsp->nvme_fc_private = iod; 196662306a36Sopenharmony_ci iod->lsrsp->rspbuf = iod->rspbuf; 196762306a36Sopenharmony_ci iod->lsrsp->rspdma = iod->rspdma; 196862306a36Sopenharmony_ci iod->lsrsp->done = nvmet_fc_xmt_ls_rsp_done; 196962306a36Sopenharmony_ci /* Be preventative. handlers will later set to valid length */ 197062306a36Sopenharmony_ci iod->lsrsp->rsplen = 0; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci iod->assoc = NULL; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci /* 197562306a36Sopenharmony_ci * handlers: 197662306a36Sopenharmony_ci * parse request input, execute the request, and format the 197762306a36Sopenharmony_ci * LS response 197862306a36Sopenharmony_ci */ 197962306a36Sopenharmony_ci switch (w0->ls_cmd) { 198062306a36Sopenharmony_ci case FCNVME_LS_CREATE_ASSOCIATION: 198162306a36Sopenharmony_ci /* Creates Association and initial Admin Queue/Connection */ 198262306a36Sopenharmony_ci nvmet_fc_ls_create_association(tgtport, iod); 198362306a36Sopenharmony_ci break; 198462306a36Sopenharmony_ci case FCNVME_LS_CREATE_CONNECTION: 198562306a36Sopenharmony_ci /* Creates an IO Queue/Connection */ 198662306a36Sopenharmony_ci nvmet_fc_ls_create_connection(tgtport, iod); 198762306a36Sopenharmony_ci break; 198862306a36Sopenharmony_ci case FCNVME_LS_DISCONNECT_ASSOC: 198962306a36Sopenharmony_ci /* Terminate a Queue/Connection or the Association */ 199062306a36Sopenharmony_ci sendrsp = nvmet_fc_ls_disconnect(tgtport, iod); 199162306a36Sopenharmony_ci break; 199262306a36Sopenharmony_ci default: 199362306a36Sopenharmony_ci iod->lsrsp->rsplen = nvme_fc_format_rjt(iod->rspbuf, 199462306a36Sopenharmony_ci sizeof(*iod->rspbuf), w0->ls_cmd, 199562306a36Sopenharmony_ci FCNVME_RJT_RC_INVAL, FCNVME_RJT_EXP_NONE, 0); 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci if (sendrsp) 199962306a36Sopenharmony_ci nvmet_fc_xmt_ls_rsp(tgtport, iod); 200062306a36Sopenharmony_ci} 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci/* 200362306a36Sopenharmony_ci * Actual processing routine for received FC-NVME LS Requests from the LLD 200462306a36Sopenharmony_ci */ 200562306a36Sopenharmony_cistatic void 200662306a36Sopenharmony_cinvmet_fc_handle_ls_rqst_work(struct work_struct *work) 200762306a36Sopenharmony_ci{ 200862306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod = 200962306a36Sopenharmony_ci container_of(work, struct nvmet_fc_ls_iod, work); 201062306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = iod->tgtport; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci nvmet_fc_handle_ls_rqst(tgtport, iod); 201362306a36Sopenharmony_ci} 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci/** 201762306a36Sopenharmony_ci * nvmet_fc_rcv_ls_req - transport entry point called by an LLDD 201862306a36Sopenharmony_ci * upon the reception of a NVME LS request. 201962306a36Sopenharmony_ci * 202062306a36Sopenharmony_ci * The nvmet-fc layer will copy payload to an internal structure for 202162306a36Sopenharmony_ci * processing. As such, upon completion of the routine, the LLDD may 202262306a36Sopenharmony_ci * immediately free/reuse the LS request buffer passed in the call. 202362306a36Sopenharmony_ci * 202462306a36Sopenharmony_ci * If this routine returns error, the LLDD should abort the exchange. 202562306a36Sopenharmony_ci * 202662306a36Sopenharmony_ci * @target_port: pointer to the (registered) target port the LS was 202762306a36Sopenharmony_ci * received on. 202862306a36Sopenharmony_ci * @hosthandle: pointer to the host specific data, gets stored in iod. 202962306a36Sopenharmony_ci * @lsrsp: pointer to a lsrsp structure to be used to reference 203062306a36Sopenharmony_ci * the exchange corresponding to the LS. 203162306a36Sopenharmony_ci * @lsreqbuf: pointer to the buffer containing the LS Request 203262306a36Sopenharmony_ci * @lsreqbuf_len: length, in bytes, of the received LS request 203362306a36Sopenharmony_ci */ 203462306a36Sopenharmony_ciint 203562306a36Sopenharmony_cinvmet_fc_rcv_ls_req(struct nvmet_fc_target_port *target_port, 203662306a36Sopenharmony_ci void *hosthandle, 203762306a36Sopenharmony_ci struct nvmefc_ls_rsp *lsrsp, 203862306a36Sopenharmony_ci void *lsreqbuf, u32 lsreqbuf_len) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = targetport_to_tgtport(target_port); 204162306a36Sopenharmony_ci struct nvmet_fc_ls_iod *iod; 204262306a36Sopenharmony_ci struct fcnvme_ls_rqst_w0 *w0 = (struct fcnvme_ls_rqst_w0 *)lsreqbuf; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci if (lsreqbuf_len > sizeof(union nvmefc_ls_requests)) { 204562306a36Sopenharmony_ci dev_info(tgtport->dev, 204662306a36Sopenharmony_ci "RCV %s LS failed: payload too large (%d)\n", 204762306a36Sopenharmony_ci (w0->ls_cmd <= NVME_FC_LAST_LS_CMD_VALUE) ? 204862306a36Sopenharmony_ci nvmefc_ls_names[w0->ls_cmd] : "", 204962306a36Sopenharmony_ci lsreqbuf_len); 205062306a36Sopenharmony_ci return -E2BIG; 205162306a36Sopenharmony_ci } 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci if (!nvmet_fc_tgtport_get(tgtport)) { 205462306a36Sopenharmony_ci dev_info(tgtport->dev, 205562306a36Sopenharmony_ci "RCV %s LS failed: target deleting\n", 205662306a36Sopenharmony_ci (w0->ls_cmd <= NVME_FC_LAST_LS_CMD_VALUE) ? 205762306a36Sopenharmony_ci nvmefc_ls_names[w0->ls_cmd] : ""); 205862306a36Sopenharmony_ci return -ESHUTDOWN; 205962306a36Sopenharmony_ci } 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci iod = nvmet_fc_alloc_ls_iod(tgtport); 206262306a36Sopenharmony_ci if (!iod) { 206362306a36Sopenharmony_ci dev_info(tgtport->dev, 206462306a36Sopenharmony_ci "RCV %s LS failed: context allocation failed\n", 206562306a36Sopenharmony_ci (w0->ls_cmd <= NVME_FC_LAST_LS_CMD_VALUE) ? 206662306a36Sopenharmony_ci nvmefc_ls_names[w0->ls_cmd] : ""); 206762306a36Sopenharmony_ci nvmet_fc_tgtport_put(tgtport); 206862306a36Sopenharmony_ci return -ENOENT; 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci iod->lsrsp = lsrsp; 207262306a36Sopenharmony_ci iod->fcpreq = NULL; 207362306a36Sopenharmony_ci memcpy(iod->rqstbuf, lsreqbuf, lsreqbuf_len); 207462306a36Sopenharmony_ci iod->rqstdatalen = lsreqbuf_len; 207562306a36Sopenharmony_ci iod->hosthandle = hosthandle; 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci queue_work(nvmet_wq, &iod->work); 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci return 0; 208062306a36Sopenharmony_ci} 208162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmet_fc_rcv_ls_req); 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci/* 208562306a36Sopenharmony_ci * ********************** 208662306a36Sopenharmony_ci * Start of FCP handling 208762306a36Sopenharmony_ci * ********************** 208862306a36Sopenharmony_ci */ 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_cistatic int 209162306a36Sopenharmony_cinvmet_fc_alloc_tgt_pgs(struct nvmet_fc_fcp_iod *fod) 209262306a36Sopenharmony_ci{ 209362306a36Sopenharmony_ci struct scatterlist *sg; 209462306a36Sopenharmony_ci unsigned int nent; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci sg = sgl_alloc(fod->req.transfer_len, GFP_KERNEL, &nent); 209762306a36Sopenharmony_ci if (!sg) 209862306a36Sopenharmony_ci goto out; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci fod->data_sg = sg; 210162306a36Sopenharmony_ci fod->data_sg_cnt = nent; 210262306a36Sopenharmony_ci fod->data_sg_cnt = fc_dma_map_sg(fod->tgtport->dev, sg, nent, 210362306a36Sopenharmony_ci ((fod->io_dir == NVMET_FCP_WRITE) ? 210462306a36Sopenharmony_ci DMA_FROM_DEVICE : DMA_TO_DEVICE)); 210562306a36Sopenharmony_ci /* note: write from initiator perspective */ 210662306a36Sopenharmony_ci fod->next_sg = fod->data_sg; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci return 0; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ciout: 211162306a36Sopenharmony_ci return NVME_SC_INTERNAL; 211262306a36Sopenharmony_ci} 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_cistatic void 211562306a36Sopenharmony_cinvmet_fc_free_tgt_pgs(struct nvmet_fc_fcp_iod *fod) 211662306a36Sopenharmony_ci{ 211762306a36Sopenharmony_ci if (!fod->data_sg || !fod->data_sg_cnt) 211862306a36Sopenharmony_ci return; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci fc_dma_unmap_sg(fod->tgtport->dev, fod->data_sg, fod->data_sg_cnt, 212162306a36Sopenharmony_ci ((fod->io_dir == NVMET_FCP_WRITE) ? 212262306a36Sopenharmony_ci DMA_FROM_DEVICE : DMA_TO_DEVICE)); 212362306a36Sopenharmony_ci sgl_free(fod->data_sg); 212462306a36Sopenharmony_ci fod->data_sg = NULL; 212562306a36Sopenharmony_ci fod->data_sg_cnt = 0; 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_cistatic bool 213062306a36Sopenharmony_ciqueue_90percent_full(struct nvmet_fc_tgt_queue *q, u32 sqhd) 213162306a36Sopenharmony_ci{ 213262306a36Sopenharmony_ci u32 sqtail, used; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci /* egad, this is ugly. And sqtail is just a best guess */ 213562306a36Sopenharmony_ci sqtail = atomic_read(&q->sqtail) % q->sqsize; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci used = (sqtail < sqhd) ? (sqtail + q->sqsize - sqhd) : (sqtail - sqhd); 213862306a36Sopenharmony_ci return ((used * 10) >= (((u32)(q->sqsize - 1) * 9))); 213962306a36Sopenharmony_ci} 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci/* 214262306a36Sopenharmony_ci * Prep RSP payload. 214362306a36Sopenharmony_ci * May be a NVMET_FCOP_RSP or NVMET_FCOP_READDATA_RSP op 214462306a36Sopenharmony_ci */ 214562306a36Sopenharmony_cistatic void 214662306a36Sopenharmony_cinvmet_fc_prep_fcp_rsp(struct nvmet_fc_tgtport *tgtport, 214762306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod) 214862306a36Sopenharmony_ci{ 214962306a36Sopenharmony_ci struct nvme_fc_ersp_iu *ersp = &fod->rspiubuf; 215062306a36Sopenharmony_ci struct nvme_common_command *sqe = &fod->cmdiubuf.sqe.common; 215162306a36Sopenharmony_ci struct nvme_completion *cqe = &ersp->cqe; 215262306a36Sopenharmony_ci u32 *cqewd = (u32 *)cqe; 215362306a36Sopenharmony_ci bool send_ersp = false; 215462306a36Sopenharmony_ci u32 rsn, rspcnt, xfr_length; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (fod->fcpreq->op == NVMET_FCOP_READDATA_RSP) 215762306a36Sopenharmony_ci xfr_length = fod->req.transfer_len; 215862306a36Sopenharmony_ci else 215962306a36Sopenharmony_ci xfr_length = fod->offset; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci /* 216262306a36Sopenharmony_ci * check to see if we can send a 0's rsp. 216362306a36Sopenharmony_ci * Note: to send a 0's response, the NVME-FC host transport will 216462306a36Sopenharmony_ci * recreate the CQE. The host transport knows: sq id, SQHD (last 216562306a36Sopenharmony_ci * seen in an ersp), and command_id. Thus it will create a 216662306a36Sopenharmony_ci * zero-filled CQE with those known fields filled in. Transport 216762306a36Sopenharmony_ci * must send an ersp for any condition where the cqe won't match 216862306a36Sopenharmony_ci * this. 216962306a36Sopenharmony_ci * 217062306a36Sopenharmony_ci * Here are the FC-NVME mandated cases where we must send an ersp: 217162306a36Sopenharmony_ci * every N responses, where N=ersp_ratio 217262306a36Sopenharmony_ci * force fabric commands to send ersp's (not in FC-NVME but good 217362306a36Sopenharmony_ci * practice) 217462306a36Sopenharmony_ci * normal cmds: any time status is non-zero, or status is zero 217562306a36Sopenharmony_ci * but words 0 or 1 are non-zero. 217662306a36Sopenharmony_ci * the SQ is 90% or more full 217762306a36Sopenharmony_ci * the cmd is a fused command 217862306a36Sopenharmony_ci * transferred data length not equal to cmd iu length 217962306a36Sopenharmony_ci */ 218062306a36Sopenharmony_ci rspcnt = atomic_inc_return(&fod->queue->zrspcnt); 218162306a36Sopenharmony_ci if (!(rspcnt % fod->queue->ersp_ratio) || 218262306a36Sopenharmony_ci nvme_is_fabrics((struct nvme_command *) sqe) || 218362306a36Sopenharmony_ci xfr_length != fod->req.transfer_len || 218462306a36Sopenharmony_ci (le16_to_cpu(cqe->status) & 0xFFFE) || cqewd[0] || cqewd[1] || 218562306a36Sopenharmony_ci (sqe->flags & (NVME_CMD_FUSE_FIRST | NVME_CMD_FUSE_SECOND)) || 218662306a36Sopenharmony_ci queue_90percent_full(fod->queue, le16_to_cpu(cqe->sq_head))) 218762306a36Sopenharmony_ci send_ersp = true; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci /* re-set the fields */ 219062306a36Sopenharmony_ci fod->fcpreq->rspaddr = ersp; 219162306a36Sopenharmony_ci fod->fcpreq->rspdma = fod->rspdma; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci if (!send_ersp) { 219462306a36Sopenharmony_ci memset(ersp, 0, NVME_FC_SIZEOF_ZEROS_RSP); 219562306a36Sopenharmony_ci fod->fcpreq->rsplen = NVME_FC_SIZEOF_ZEROS_RSP; 219662306a36Sopenharmony_ci } else { 219762306a36Sopenharmony_ci ersp->iu_len = cpu_to_be16(sizeof(*ersp)/sizeof(u32)); 219862306a36Sopenharmony_ci rsn = atomic_inc_return(&fod->queue->rsn); 219962306a36Sopenharmony_ci ersp->rsn = cpu_to_be32(rsn); 220062306a36Sopenharmony_ci ersp->xfrd_len = cpu_to_be32(xfr_length); 220162306a36Sopenharmony_ci fod->fcpreq->rsplen = sizeof(*ersp); 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci fc_dma_sync_single_for_device(tgtport->dev, fod->rspdma, 220562306a36Sopenharmony_ci sizeof(fod->rspiubuf), DMA_TO_DEVICE); 220662306a36Sopenharmony_ci} 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_cistatic void nvmet_fc_xmt_fcp_op_done(struct nvmefc_tgt_fcp_req *fcpreq); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_cistatic void 221162306a36Sopenharmony_cinvmet_fc_abort_op(struct nvmet_fc_tgtport *tgtport, 221262306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod) 221362306a36Sopenharmony_ci{ 221462306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq = fod->fcpreq; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci /* data no longer needed */ 221762306a36Sopenharmony_ci nvmet_fc_free_tgt_pgs(fod); 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci /* 222062306a36Sopenharmony_ci * if an ABTS was received or we issued the fcp_abort early 222162306a36Sopenharmony_ci * don't call abort routine again. 222262306a36Sopenharmony_ci */ 222362306a36Sopenharmony_ci /* no need to take lock - lock was taken earlier to get here */ 222462306a36Sopenharmony_ci if (!fod->aborted) 222562306a36Sopenharmony_ci tgtport->ops->fcp_abort(&tgtport->fc_target_port, fcpreq); 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci nvmet_fc_free_fcp_iod(fod->queue, fod); 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_cistatic void 223162306a36Sopenharmony_cinvmet_fc_xmt_fcp_rsp(struct nvmet_fc_tgtport *tgtport, 223262306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci int ret; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci fod->fcpreq->op = NVMET_FCOP_RSP; 223762306a36Sopenharmony_ci fod->fcpreq->timeout = 0; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci nvmet_fc_prep_fcp_rsp(tgtport, fod); 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci ret = tgtport->ops->fcp_op(&tgtport->fc_target_port, fod->fcpreq); 224262306a36Sopenharmony_ci if (ret) 224362306a36Sopenharmony_ci nvmet_fc_abort_op(tgtport, fod); 224462306a36Sopenharmony_ci} 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_cistatic void 224762306a36Sopenharmony_cinvmet_fc_transfer_fcp_data(struct nvmet_fc_tgtport *tgtport, 224862306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod, u8 op) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq = fod->fcpreq; 225162306a36Sopenharmony_ci struct scatterlist *sg = fod->next_sg; 225262306a36Sopenharmony_ci unsigned long flags; 225362306a36Sopenharmony_ci u32 remaininglen = fod->req.transfer_len - fod->offset; 225462306a36Sopenharmony_ci u32 tlen = 0; 225562306a36Sopenharmony_ci int ret; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci fcpreq->op = op; 225862306a36Sopenharmony_ci fcpreq->offset = fod->offset; 225962306a36Sopenharmony_ci fcpreq->timeout = NVME_FC_TGTOP_TIMEOUT_SEC; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci /* 226262306a36Sopenharmony_ci * for next sequence: 226362306a36Sopenharmony_ci * break at a sg element boundary 226462306a36Sopenharmony_ci * attempt to keep sequence length capped at 226562306a36Sopenharmony_ci * NVMET_FC_MAX_SEQ_LENGTH but allow sequence to 226662306a36Sopenharmony_ci * be longer if a single sg element is larger 226762306a36Sopenharmony_ci * than that amount. This is done to avoid creating 226862306a36Sopenharmony_ci * a new sg list to use for the tgtport api. 226962306a36Sopenharmony_ci */ 227062306a36Sopenharmony_ci fcpreq->sg = sg; 227162306a36Sopenharmony_ci fcpreq->sg_cnt = 0; 227262306a36Sopenharmony_ci while (tlen < remaininglen && 227362306a36Sopenharmony_ci fcpreq->sg_cnt < tgtport->max_sg_cnt && 227462306a36Sopenharmony_ci tlen + sg_dma_len(sg) < NVMET_FC_MAX_SEQ_LENGTH) { 227562306a36Sopenharmony_ci fcpreq->sg_cnt++; 227662306a36Sopenharmony_ci tlen += sg_dma_len(sg); 227762306a36Sopenharmony_ci sg = sg_next(sg); 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci if (tlen < remaininglen && fcpreq->sg_cnt == 0) { 228062306a36Sopenharmony_ci fcpreq->sg_cnt++; 228162306a36Sopenharmony_ci tlen += min_t(u32, sg_dma_len(sg), remaininglen); 228262306a36Sopenharmony_ci sg = sg_next(sg); 228362306a36Sopenharmony_ci } 228462306a36Sopenharmony_ci if (tlen < remaininglen) 228562306a36Sopenharmony_ci fod->next_sg = sg; 228662306a36Sopenharmony_ci else 228762306a36Sopenharmony_ci fod->next_sg = NULL; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci fcpreq->transfer_length = tlen; 229062306a36Sopenharmony_ci fcpreq->transferred_length = 0; 229162306a36Sopenharmony_ci fcpreq->fcp_error = 0; 229262306a36Sopenharmony_ci fcpreq->rsplen = 0; 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci /* 229562306a36Sopenharmony_ci * If the last READDATA request: check if LLDD supports 229662306a36Sopenharmony_ci * combined xfr with response. 229762306a36Sopenharmony_ci */ 229862306a36Sopenharmony_ci if ((op == NVMET_FCOP_READDATA) && 229962306a36Sopenharmony_ci ((fod->offset + fcpreq->transfer_length) == fod->req.transfer_len) && 230062306a36Sopenharmony_ci (tgtport->ops->target_features & NVMET_FCTGTFEAT_READDATA_RSP)) { 230162306a36Sopenharmony_ci fcpreq->op = NVMET_FCOP_READDATA_RSP; 230262306a36Sopenharmony_ci nvmet_fc_prep_fcp_rsp(tgtport, fod); 230362306a36Sopenharmony_ci } 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci ret = tgtport->ops->fcp_op(&tgtport->fc_target_port, fod->fcpreq); 230662306a36Sopenharmony_ci if (ret) { 230762306a36Sopenharmony_ci /* 230862306a36Sopenharmony_ci * should be ok to set w/o lock as its in the thread of 230962306a36Sopenharmony_ci * execution (not an async timer routine) and doesn't 231062306a36Sopenharmony_ci * contend with any clearing action 231162306a36Sopenharmony_ci */ 231262306a36Sopenharmony_ci fod->abort = true; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci if (op == NVMET_FCOP_WRITEDATA) { 231562306a36Sopenharmony_ci spin_lock_irqsave(&fod->flock, flags); 231662306a36Sopenharmony_ci fod->writedataactive = false; 231762306a36Sopenharmony_ci spin_unlock_irqrestore(&fod->flock, flags); 231862306a36Sopenharmony_ci nvmet_req_complete(&fod->req, NVME_SC_INTERNAL); 231962306a36Sopenharmony_ci } else /* NVMET_FCOP_READDATA or NVMET_FCOP_READDATA_RSP */ { 232062306a36Sopenharmony_ci fcpreq->fcp_error = ret; 232162306a36Sopenharmony_ci fcpreq->transferred_length = 0; 232262306a36Sopenharmony_ci nvmet_fc_xmt_fcp_op_done(fod->fcpreq); 232362306a36Sopenharmony_ci } 232462306a36Sopenharmony_ci } 232562306a36Sopenharmony_ci} 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_cistatic inline bool 232862306a36Sopenharmony_ci__nvmet_fc_fod_op_abort(struct nvmet_fc_fcp_iod *fod, bool abort) 232962306a36Sopenharmony_ci{ 233062306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq = fod->fcpreq; 233162306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = fod->tgtport; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci /* if in the middle of an io and we need to tear down */ 233462306a36Sopenharmony_ci if (abort) { 233562306a36Sopenharmony_ci if (fcpreq->op == NVMET_FCOP_WRITEDATA) { 233662306a36Sopenharmony_ci nvmet_req_complete(&fod->req, NVME_SC_INTERNAL); 233762306a36Sopenharmony_ci return true; 233862306a36Sopenharmony_ci } 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_ci nvmet_fc_abort_op(tgtport, fod); 234162306a36Sopenharmony_ci return true; 234262306a36Sopenharmony_ci } 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci return false; 234562306a36Sopenharmony_ci} 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci/* 234862306a36Sopenharmony_ci * actual done handler for FCP operations when completed by the lldd 234962306a36Sopenharmony_ci */ 235062306a36Sopenharmony_cistatic void 235162306a36Sopenharmony_cinvmet_fc_fod_op_done(struct nvmet_fc_fcp_iod *fod) 235262306a36Sopenharmony_ci{ 235362306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq = fod->fcpreq; 235462306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = fod->tgtport; 235562306a36Sopenharmony_ci unsigned long flags; 235662306a36Sopenharmony_ci bool abort; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci spin_lock_irqsave(&fod->flock, flags); 235962306a36Sopenharmony_ci abort = fod->abort; 236062306a36Sopenharmony_ci fod->writedataactive = false; 236162306a36Sopenharmony_ci spin_unlock_irqrestore(&fod->flock, flags); 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci switch (fcpreq->op) { 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci case NVMET_FCOP_WRITEDATA: 236662306a36Sopenharmony_ci if (__nvmet_fc_fod_op_abort(fod, abort)) 236762306a36Sopenharmony_ci return; 236862306a36Sopenharmony_ci if (fcpreq->fcp_error || 236962306a36Sopenharmony_ci fcpreq->transferred_length != fcpreq->transfer_length) { 237062306a36Sopenharmony_ci spin_lock_irqsave(&fod->flock, flags); 237162306a36Sopenharmony_ci fod->abort = true; 237262306a36Sopenharmony_ci spin_unlock_irqrestore(&fod->flock, flags); 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci nvmet_req_complete(&fod->req, NVME_SC_INTERNAL); 237562306a36Sopenharmony_ci return; 237662306a36Sopenharmony_ci } 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci fod->offset += fcpreq->transferred_length; 237962306a36Sopenharmony_ci if (fod->offset != fod->req.transfer_len) { 238062306a36Sopenharmony_ci spin_lock_irqsave(&fod->flock, flags); 238162306a36Sopenharmony_ci fod->writedataactive = true; 238262306a36Sopenharmony_ci spin_unlock_irqrestore(&fod->flock, flags); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci /* transfer the next chunk */ 238562306a36Sopenharmony_ci nvmet_fc_transfer_fcp_data(tgtport, fod, 238662306a36Sopenharmony_ci NVMET_FCOP_WRITEDATA); 238762306a36Sopenharmony_ci return; 238862306a36Sopenharmony_ci } 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci /* data transfer complete, resume with nvmet layer */ 239162306a36Sopenharmony_ci fod->req.execute(&fod->req); 239262306a36Sopenharmony_ci break; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci case NVMET_FCOP_READDATA: 239562306a36Sopenharmony_ci case NVMET_FCOP_READDATA_RSP: 239662306a36Sopenharmony_ci if (__nvmet_fc_fod_op_abort(fod, abort)) 239762306a36Sopenharmony_ci return; 239862306a36Sopenharmony_ci if (fcpreq->fcp_error || 239962306a36Sopenharmony_ci fcpreq->transferred_length != fcpreq->transfer_length) { 240062306a36Sopenharmony_ci nvmet_fc_abort_op(tgtport, fod); 240162306a36Sopenharmony_ci return; 240262306a36Sopenharmony_ci } 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci /* success */ 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci if (fcpreq->op == NVMET_FCOP_READDATA_RSP) { 240762306a36Sopenharmony_ci /* data no longer needed */ 240862306a36Sopenharmony_ci nvmet_fc_free_tgt_pgs(fod); 240962306a36Sopenharmony_ci nvmet_fc_free_fcp_iod(fod->queue, fod); 241062306a36Sopenharmony_ci return; 241162306a36Sopenharmony_ci } 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci fod->offset += fcpreq->transferred_length; 241462306a36Sopenharmony_ci if (fod->offset != fod->req.transfer_len) { 241562306a36Sopenharmony_ci /* transfer the next chunk */ 241662306a36Sopenharmony_ci nvmet_fc_transfer_fcp_data(tgtport, fod, 241762306a36Sopenharmony_ci NVMET_FCOP_READDATA); 241862306a36Sopenharmony_ci return; 241962306a36Sopenharmony_ci } 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci /* data transfer complete, send response */ 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci /* data no longer needed */ 242462306a36Sopenharmony_ci nvmet_fc_free_tgt_pgs(fod); 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci nvmet_fc_xmt_fcp_rsp(tgtport, fod); 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci break; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci case NVMET_FCOP_RSP: 243162306a36Sopenharmony_ci if (__nvmet_fc_fod_op_abort(fod, abort)) 243262306a36Sopenharmony_ci return; 243362306a36Sopenharmony_ci nvmet_fc_free_fcp_iod(fod->queue, fod); 243462306a36Sopenharmony_ci break; 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci default: 243762306a36Sopenharmony_ci break; 243862306a36Sopenharmony_ci } 243962306a36Sopenharmony_ci} 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_cistatic void 244262306a36Sopenharmony_cinvmet_fc_xmt_fcp_op_done(struct nvmefc_tgt_fcp_req *fcpreq) 244362306a36Sopenharmony_ci{ 244462306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod = fcpreq->nvmet_fc_private; 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci nvmet_fc_fod_op_done(fod); 244762306a36Sopenharmony_ci} 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci/* 245062306a36Sopenharmony_ci * actual completion handler after execution by the nvmet layer 245162306a36Sopenharmony_ci */ 245262306a36Sopenharmony_cistatic void 245362306a36Sopenharmony_ci__nvmet_fc_fcp_nvme_cmd_done(struct nvmet_fc_tgtport *tgtport, 245462306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod, int status) 245562306a36Sopenharmony_ci{ 245662306a36Sopenharmony_ci struct nvme_common_command *sqe = &fod->cmdiubuf.sqe.common; 245762306a36Sopenharmony_ci struct nvme_completion *cqe = &fod->rspiubuf.cqe; 245862306a36Sopenharmony_ci unsigned long flags; 245962306a36Sopenharmony_ci bool abort; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci spin_lock_irqsave(&fod->flock, flags); 246262306a36Sopenharmony_ci abort = fod->abort; 246362306a36Sopenharmony_ci spin_unlock_irqrestore(&fod->flock, flags); 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci /* if we have a CQE, snoop the last sq_head value */ 246662306a36Sopenharmony_ci if (!status) 246762306a36Sopenharmony_ci fod->queue->sqhd = cqe->sq_head; 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci if (abort) { 247062306a36Sopenharmony_ci nvmet_fc_abort_op(tgtport, fod); 247162306a36Sopenharmony_ci return; 247262306a36Sopenharmony_ci } 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci /* if an error handling the cmd post initial parsing */ 247562306a36Sopenharmony_ci if (status) { 247662306a36Sopenharmony_ci /* fudge up a failed CQE status for our transport error */ 247762306a36Sopenharmony_ci memset(cqe, 0, sizeof(*cqe)); 247862306a36Sopenharmony_ci cqe->sq_head = fod->queue->sqhd; /* echo last cqe sqhd */ 247962306a36Sopenharmony_ci cqe->sq_id = cpu_to_le16(fod->queue->qid); 248062306a36Sopenharmony_ci cqe->command_id = sqe->command_id; 248162306a36Sopenharmony_ci cqe->status = cpu_to_le16(status); 248262306a36Sopenharmony_ci } else { 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci /* 248562306a36Sopenharmony_ci * try to push the data even if the SQE status is non-zero. 248662306a36Sopenharmony_ci * There may be a status where data still was intended to 248762306a36Sopenharmony_ci * be moved 248862306a36Sopenharmony_ci */ 248962306a36Sopenharmony_ci if ((fod->io_dir == NVMET_FCP_READ) && (fod->data_sg_cnt)) { 249062306a36Sopenharmony_ci /* push the data over before sending rsp */ 249162306a36Sopenharmony_ci nvmet_fc_transfer_fcp_data(tgtport, fod, 249262306a36Sopenharmony_ci NVMET_FCOP_READDATA); 249362306a36Sopenharmony_ci return; 249462306a36Sopenharmony_ci } 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci /* writes & no data - fall thru */ 249762306a36Sopenharmony_ci } 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci /* data no longer needed */ 250062306a36Sopenharmony_ci nvmet_fc_free_tgt_pgs(fod); 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci nvmet_fc_xmt_fcp_rsp(tgtport, fod); 250362306a36Sopenharmony_ci} 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_cistatic void 250762306a36Sopenharmony_cinvmet_fc_fcp_nvme_cmd_done(struct nvmet_req *nvme_req) 250862306a36Sopenharmony_ci{ 250962306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod = nvmet_req_to_fod(nvme_req); 251062306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = fod->tgtport; 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci __nvmet_fc_fcp_nvme_cmd_done(tgtport, fod, 0); 251362306a36Sopenharmony_ci} 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci/* 251762306a36Sopenharmony_ci * Actual processing routine for received FC-NVME I/O Requests from the LLD 251862306a36Sopenharmony_ci */ 251962306a36Sopenharmony_cistatic void 252062306a36Sopenharmony_cinvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, 252162306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod) 252262306a36Sopenharmony_ci{ 252362306a36Sopenharmony_ci struct nvme_fc_cmd_iu *cmdiu = &fod->cmdiubuf; 252462306a36Sopenharmony_ci u32 xfrlen = be32_to_cpu(cmdiu->data_len); 252562306a36Sopenharmony_ci int ret; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci /* 252862306a36Sopenharmony_ci * Fused commands are currently not supported in the linux 252962306a36Sopenharmony_ci * implementation. 253062306a36Sopenharmony_ci * 253162306a36Sopenharmony_ci * As such, the implementation of the FC transport does not 253262306a36Sopenharmony_ci * look at the fused commands and order delivery to the upper 253362306a36Sopenharmony_ci * layer until we have both based on csn. 253462306a36Sopenharmony_ci */ 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci fod->fcpreq->done = nvmet_fc_xmt_fcp_op_done; 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci if (cmdiu->flags & FCNVME_CMD_FLAGS_WRITE) { 253962306a36Sopenharmony_ci fod->io_dir = NVMET_FCP_WRITE; 254062306a36Sopenharmony_ci if (!nvme_is_write(&cmdiu->sqe)) 254162306a36Sopenharmony_ci goto transport_error; 254262306a36Sopenharmony_ci } else if (cmdiu->flags & FCNVME_CMD_FLAGS_READ) { 254362306a36Sopenharmony_ci fod->io_dir = NVMET_FCP_READ; 254462306a36Sopenharmony_ci if (nvme_is_write(&cmdiu->sqe)) 254562306a36Sopenharmony_ci goto transport_error; 254662306a36Sopenharmony_ci } else { 254762306a36Sopenharmony_ci fod->io_dir = NVMET_FCP_NODATA; 254862306a36Sopenharmony_ci if (xfrlen) 254962306a36Sopenharmony_ci goto transport_error; 255062306a36Sopenharmony_ci } 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci fod->req.cmd = &fod->cmdiubuf.sqe; 255362306a36Sopenharmony_ci fod->req.cqe = &fod->rspiubuf.cqe; 255462306a36Sopenharmony_ci if (!tgtport->pe) 255562306a36Sopenharmony_ci goto transport_error; 255662306a36Sopenharmony_ci fod->req.port = tgtport->pe->port; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci /* clear any response payload */ 255962306a36Sopenharmony_ci memset(&fod->rspiubuf, 0, sizeof(fod->rspiubuf)); 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci fod->data_sg = NULL; 256262306a36Sopenharmony_ci fod->data_sg_cnt = 0; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci ret = nvmet_req_init(&fod->req, 256562306a36Sopenharmony_ci &fod->queue->nvme_cq, 256662306a36Sopenharmony_ci &fod->queue->nvme_sq, 256762306a36Sopenharmony_ci &nvmet_fc_tgt_fcp_ops); 256862306a36Sopenharmony_ci if (!ret) { 256962306a36Sopenharmony_ci /* bad SQE content or invalid ctrl state */ 257062306a36Sopenharmony_ci /* nvmet layer has already called op done to send rsp. */ 257162306a36Sopenharmony_ci return; 257262306a36Sopenharmony_ci } 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci fod->req.transfer_len = xfrlen; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci /* keep a running counter of tail position */ 257762306a36Sopenharmony_ci atomic_inc(&fod->queue->sqtail); 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci if (fod->req.transfer_len) { 258062306a36Sopenharmony_ci ret = nvmet_fc_alloc_tgt_pgs(fod); 258162306a36Sopenharmony_ci if (ret) { 258262306a36Sopenharmony_ci nvmet_req_complete(&fod->req, ret); 258362306a36Sopenharmony_ci return; 258462306a36Sopenharmony_ci } 258562306a36Sopenharmony_ci } 258662306a36Sopenharmony_ci fod->req.sg = fod->data_sg; 258762306a36Sopenharmony_ci fod->req.sg_cnt = fod->data_sg_cnt; 258862306a36Sopenharmony_ci fod->offset = 0; 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci if (fod->io_dir == NVMET_FCP_WRITE) { 259162306a36Sopenharmony_ci /* pull the data over before invoking nvmet layer */ 259262306a36Sopenharmony_ci nvmet_fc_transfer_fcp_data(tgtport, fod, NVMET_FCOP_WRITEDATA); 259362306a36Sopenharmony_ci return; 259462306a36Sopenharmony_ci } 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci /* 259762306a36Sopenharmony_ci * Reads or no data: 259862306a36Sopenharmony_ci * 259962306a36Sopenharmony_ci * can invoke the nvmet_layer now. If read data, cmd completion will 260062306a36Sopenharmony_ci * push the data 260162306a36Sopenharmony_ci */ 260262306a36Sopenharmony_ci fod->req.execute(&fod->req); 260362306a36Sopenharmony_ci return; 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_citransport_error: 260662306a36Sopenharmony_ci nvmet_fc_abort_op(tgtport, fod); 260762306a36Sopenharmony_ci} 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci/** 261062306a36Sopenharmony_ci * nvmet_fc_rcv_fcp_req - transport entry point called by an LLDD 261162306a36Sopenharmony_ci * upon the reception of a NVME FCP CMD IU. 261262306a36Sopenharmony_ci * 261362306a36Sopenharmony_ci * Pass a FC-NVME FCP CMD IU received from the FC link to the nvmet-fc 261462306a36Sopenharmony_ci * layer for processing. 261562306a36Sopenharmony_ci * 261662306a36Sopenharmony_ci * The nvmet_fc layer allocates a local job structure (struct 261762306a36Sopenharmony_ci * nvmet_fc_fcp_iod) from the queue for the io and copies the 261862306a36Sopenharmony_ci * CMD IU buffer to the job structure. As such, on a successful 261962306a36Sopenharmony_ci * completion (returns 0), the LLDD may immediately free/reuse 262062306a36Sopenharmony_ci * the CMD IU buffer passed in the call. 262162306a36Sopenharmony_ci * 262262306a36Sopenharmony_ci * However, in some circumstances, due to the packetized nature of FC 262362306a36Sopenharmony_ci * and the api of the FC LLDD which may issue a hw command to send the 262462306a36Sopenharmony_ci * response, but the LLDD may not get the hw completion for that command 262562306a36Sopenharmony_ci * and upcall the nvmet_fc layer before a new command may be 262662306a36Sopenharmony_ci * asynchronously received - its possible for a command to be received 262762306a36Sopenharmony_ci * before the LLDD and nvmet_fc have recycled the job structure. It gives 262862306a36Sopenharmony_ci * the appearance of more commands received than fits in the sq. 262962306a36Sopenharmony_ci * To alleviate this scenario, a temporary queue is maintained in the 263062306a36Sopenharmony_ci * transport for pending LLDD requests waiting for a queue job structure. 263162306a36Sopenharmony_ci * In these "overrun" cases, a temporary queue element is allocated 263262306a36Sopenharmony_ci * the LLDD request and CMD iu buffer information remembered, and the 263362306a36Sopenharmony_ci * routine returns a -EOVERFLOW status. Subsequently, when a queue job 263462306a36Sopenharmony_ci * structure is freed, it is immediately reallocated for anything on the 263562306a36Sopenharmony_ci * pending request list. The LLDDs defer_rcv() callback is called, 263662306a36Sopenharmony_ci * informing the LLDD that it may reuse the CMD IU buffer, and the io 263762306a36Sopenharmony_ci * is then started normally with the transport. 263862306a36Sopenharmony_ci * 263962306a36Sopenharmony_ci * The LLDD, when receiving an -EOVERFLOW completion status, is to treat 264062306a36Sopenharmony_ci * the completion as successful but must not reuse the CMD IU buffer 264162306a36Sopenharmony_ci * until the LLDD's defer_rcv() callback has been called for the 264262306a36Sopenharmony_ci * corresponding struct nvmefc_tgt_fcp_req pointer. 264362306a36Sopenharmony_ci * 264462306a36Sopenharmony_ci * If there is any other condition in which an error occurs, the 264562306a36Sopenharmony_ci * transport will return a non-zero status indicating the error. 264662306a36Sopenharmony_ci * In all cases other than -EOVERFLOW, the transport has not accepted the 264762306a36Sopenharmony_ci * request and the LLDD should abort the exchange. 264862306a36Sopenharmony_ci * 264962306a36Sopenharmony_ci * @target_port: pointer to the (registered) target port the FCP CMD IU 265062306a36Sopenharmony_ci * was received on. 265162306a36Sopenharmony_ci * @fcpreq: pointer to a fcpreq request structure to be used to reference 265262306a36Sopenharmony_ci * the exchange corresponding to the FCP Exchange. 265362306a36Sopenharmony_ci * @cmdiubuf: pointer to the buffer containing the FCP CMD IU 265462306a36Sopenharmony_ci * @cmdiubuf_len: length, in bytes, of the received FCP CMD IU 265562306a36Sopenharmony_ci */ 265662306a36Sopenharmony_ciint 265762306a36Sopenharmony_cinvmet_fc_rcv_fcp_req(struct nvmet_fc_target_port *target_port, 265862306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq, 265962306a36Sopenharmony_ci void *cmdiubuf, u32 cmdiubuf_len) 266062306a36Sopenharmony_ci{ 266162306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = targetport_to_tgtport(target_port); 266262306a36Sopenharmony_ci struct nvme_fc_cmd_iu *cmdiu = cmdiubuf; 266362306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue; 266462306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod; 266562306a36Sopenharmony_ci struct nvmet_fc_defer_fcp_req *deferfcp; 266662306a36Sopenharmony_ci unsigned long flags; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci /* validate iu, so the connection id can be used to find the queue */ 266962306a36Sopenharmony_ci if ((cmdiubuf_len != sizeof(*cmdiu)) || 267062306a36Sopenharmony_ci (cmdiu->format_id != NVME_CMD_FORMAT_ID) || 267162306a36Sopenharmony_ci (cmdiu->fc_id != NVME_CMD_FC_ID) || 267262306a36Sopenharmony_ci (be16_to_cpu(cmdiu->iu_len) != (sizeof(*cmdiu)/4))) 267362306a36Sopenharmony_ci return -EIO; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci queue = nvmet_fc_find_target_queue(tgtport, 267662306a36Sopenharmony_ci be64_to_cpu(cmdiu->connection_id)); 267762306a36Sopenharmony_ci if (!queue) 267862306a36Sopenharmony_ci return -ENOTCONN; 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci /* 268162306a36Sopenharmony_ci * note: reference taken by find_target_queue 268262306a36Sopenharmony_ci * After successful fod allocation, the fod will inherit the 268362306a36Sopenharmony_ci * ownership of that reference and will remove the reference 268462306a36Sopenharmony_ci * when the fod is freed. 268562306a36Sopenharmony_ci */ 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci spin_lock_irqsave(&queue->qlock, flags); 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci fod = nvmet_fc_alloc_fcp_iod(queue); 269062306a36Sopenharmony_ci if (fod) { 269162306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci fcpreq->nvmet_fc_private = fod; 269462306a36Sopenharmony_ci fod->fcpreq = fcpreq; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci memcpy(&fod->cmdiubuf, cmdiubuf, cmdiubuf_len); 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci nvmet_fc_queue_fcp_req(tgtport, queue, fcpreq); 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci return 0; 270162306a36Sopenharmony_ci } 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci if (!tgtport->ops->defer_rcv) { 270462306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 270562306a36Sopenharmony_ci /* release the queue lookup reference */ 270662306a36Sopenharmony_ci nvmet_fc_tgt_q_put(queue); 270762306a36Sopenharmony_ci return -ENOENT; 270862306a36Sopenharmony_ci } 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci deferfcp = list_first_entry_or_null(&queue->avail_defer_list, 271162306a36Sopenharmony_ci struct nvmet_fc_defer_fcp_req, req_list); 271262306a36Sopenharmony_ci if (deferfcp) { 271362306a36Sopenharmony_ci /* Just re-use one that was previously allocated */ 271462306a36Sopenharmony_ci list_del(&deferfcp->req_list); 271562306a36Sopenharmony_ci } else { 271662306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci /* Now we need to dynamically allocate one */ 271962306a36Sopenharmony_ci deferfcp = kmalloc(sizeof(*deferfcp), GFP_KERNEL); 272062306a36Sopenharmony_ci if (!deferfcp) { 272162306a36Sopenharmony_ci /* release the queue lookup reference */ 272262306a36Sopenharmony_ci nvmet_fc_tgt_q_put(queue); 272362306a36Sopenharmony_ci return -ENOMEM; 272462306a36Sopenharmony_ci } 272562306a36Sopenharmony_ci spin_lock_irqsave(&queue->qlock, flags); 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci /* For now, use rspaddr / rsplen to save payload information */ 272962306a36Sopenharmony_ci fcpreq->rspaddr = cmdiubuf; 273062306a36Sopenharmony_ci fcpreq->rsplen = cmdiubuf_len; 273162306a36Sopenharmony_ci deferfcp->fcp_req = fcpreq; 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci /* defer processing till a fod becomes available */ 273462306a36Sopenharmony_ci list_add_tail(&deferfcp->req_list, &queue->pending_cmd_list); 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci /* NOTE: the queue lookup reference is still valid */ 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci return -EOVERFLOW; 274162306a36Sopenharmony_ci} 274262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmet_fc_rcv_fcp_req); 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci/** 274562306a36Sopenharmony_ci * nvmet_fc_rcv_fcp_abort - transport entry point called by an LLDD 274662306a36Sopenharmony_ci * upon the reception of an ABTS for a FCP command 274762306a36Sopenharmony_ci * 274862306a36Sopenharmony_ci * Notify the transport that an ABTS has been received for a FCP command 274962306a36Sopenharmony_ci * that had been given to the transport via nvmet_fc_rcv_fcp_req(). The 275062306a36Sopenharmony_ci * LLDD believes the command is still being worked on 275162306a36Sopenharmony_ci * (template_ops->fcp_req_release() has not been called). 275262306a36Sopenharmony_ci * 275362306a36Sopenharmony_ci * The transport will wait for any outstanding work (an op to the LLDD, 275462306a36Sopenharmony_ci * which the lldd should complete with error due to the ABTS; or the 275562306a36Sopenharmony_ci * completion from the nvmet layer of the nvme command), then will 275662306a36Sopenharmony_ci * stop processing and call the nvmet_fc_rcv_fcp_req() callback to 275762306a36Sopenharmony_ci * return the i/o context to the LLDD. The LLDD may send the BA_ACC 275862306a36Sopenharmony_ci * to the ABTS either after return from this function (assuming any 275962306a36Sopenharmony_ci * outstanding op work has been terminated) or upon the callback being 276062306a36Sopenharmony_ci * called. 276162306a36Sopenharmony_ci * 276262306a36Sopenharmony_ci * @target_port: pointer to the (registered) target port the FCP CMD IU 276362306a36Sopenharmony_ci * was received on. 276462306a36Sopenharmony_ci * @fcpreq: pointer to the fcpreq request structure that corresponds 276562306a36Sopenharmony_ci * to the exchange that received the ABTS. 276662306a36Sopenharmony_ci */ 276762306a36Sopenharmony_civoid 276862306a36Sopenharmony_cinvmet_fc_rcv_fcp_abort(struct nvmet_fc_target_port *target_port, 276962306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *fcpreq) 277062306a36Sopenharmony_ci{ 277162306a36Sopenharmony_ci struct nvmet_fc_fcp_iod *fod = fcpreq->nvmet_fc_private; 277262306a36Sopenharmony_ci struct nvmet_fc_tgt_queue *queue; 277362306a36Sopenharmony_ci unsigned long flags; 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_ci if (!fod || fod->fcpreq != fcpreq) 277662306a36Sopenharmony_ci /* job appears to have already completed, ignore abort */ 277762306a36Sopenharmony_ci return; 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci queue = fod->queue; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci spin_lock_irqsave(&queue->qlock, flags); 278262306a36Sopenharmony_ci if (fod->active) { 278362306a36Sopenharmony_ci /* 278462306a36Sopenharmony_ci * mark as abort. The abort handler, invoked upon completion 278562306a36Sopenharmony_ci * of any work, will detect the aborted status and do the 278662306a36Sopenharmony_ci * callback. 278762306a36Sopenharmony_ci */ 278862306a36Sopenharmony_ci spin_lock(&fod->flock); 278962306a36Sopenharmony_ci fod->abort = true; 279062306a36Sopenharmony_ci fod->aborted = true; 279162306a36Sopenharmony_ci spin_unlock(&fod->flock); 279262306a36Sopenharmony_ci } 279362306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->qlock, flags); 279462306a36Sopenharmony_ci} 279562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmet_fc_rcv_fcp_abort); 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_cistruct nvmet_fc_traddr { 279962306a36Sopenharmony_ci u64 nn; 280062306a36Sopenharmony_ci u64 pn; 280162306a36Sopenharmony_ci}; 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_cistatic int 280462306a36Sopenharmony_ci__nvme_fc_parse_u64(substring_t *sstr, u64 *val) 280562306a36Sopenharmony_ci{ 280662306a36Sopenharmony_ci u64 token64; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci if (match_u64(sstr, &token64)) 280962306a36Sopenharmony_ci return -EINVAL; 281062306a36Sopenharmony_ci *val = token64; 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci return 0; 281362306a36Sopenharmony_ci} 281462306a36Sopenharmony_ci 281562306a36Sopenharmony_ci/* 281662306a36Sopenharmony_ci * This routine validates and extracts the WWN's from the TRADDR string. 281762306a36Sopenharmony_ci * As kernel parsers need the 0x to determine number base, universally 281862306a36Sopenharmony_ci * build string to parse with 0x prefix before parsing name strings. 281962306a36Sopenharmony_ci */ 282062306a36Sopenharmony_cistatic int 282162306a36Sopenharmony_cinvme_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf, size_t blen) 282262306a36Sopenharmony_ci{ 282362306a36Sopenharmony_ci char name[2 + NVME_FC_TRADDR_HEXNAMELEN + 1]; 282462306a36Sopenharmony_ci substring_t wwn = { name, &name[sizeof(name)-1] }; 282562306a36Sopenharmony_ci int nnoffset, pnoffset; 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_ci /* validate if string is one of the 2 allowed formats */ 282862306a36Sopenharmony_ci if (strnlen(buf, blen) == NVME_FC_TRADDR_MAXLENGTH && 282962306a36Sopenharmony_ci !strncmp(buf, "nn-0x", NVME_FC_TRADDR_OXNNLEN) && 283062306a36Sopenharmony_ci !strncmp(&buf[NVME_FC_TRADDR_MAX_PN_OFFSET], 283162306a36Sopenharmony_ci "pn-0x", NVME_FC_TRADDR_OXNNLEN)) { 283262306a36Sopenharmony_ci nnoffset = NVME_FC_TRADDR_OXNNLEN; 283362306a36Sopenharmony_ci pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET + 283462306a36Sopenharmony_ci NVME_FC_TRADDR_OXNNLEN; 283562306a36Sopenharmony_ci } else if ((strnlen(buf, blen) == NVME_FC_TRADDR_MINLENGTH && 283662306a36Sopenharmony_ci !strncmp(buf, "nn-", NVME_FC_TRADDR_NNLEN) && 283762306a36Sopenharmony_ci !strncmp(&buf[NVME_FC_TRADDR_MIN_PN_OFFSET], 283862306a36Sopenharmony_ci "pn-", NVME_FC_TRADDR_NNLEN))) { 283962306a36Sopenharmony_ci nnoffset = NVME_FC_TRADDR_NNLEN; 284062306a36Sopenharmony_ci pnoffset = NVME_FC_TRADDR_MIN_PN_OFFSET + NVME_FC_TRADDR_NNLEN; 284162306a36Sopenharmony_ci } else 284262306a36Sopenharmony_ci goto out_einval; 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci name[0] = '0'; 284562306a36Sopenharmony_ci name[1] = 'x'; 284662306a36Sopenharmony_ci name[2 + NVME_FC_TRADDR_HEXNAMELEN] = 0; 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci memcpy(&name[2], &buf[nnoffset], NVME_FC_TRADDR_HEXNAMELEN); 284962306a36Sopenharmony_ci if (__nvme_fc_parse_u64(&wwn, &traddr->nn)) 285062306a36Sopenharmony_ci goto out_einval; 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci memcpy(&name[2], &buf[pnoffset], NVME_FC_TRADDR_HEXNAMELEN); 285362306a36Sopenharmony_ci if (__nvme_fc_parse_u64(&wwn, &traddr->pn)) 285462306a36Sopenharmony_ci goto out_einval; 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci return 0; 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ciout_einval: 285962306a36Sopenharmony_ci pr_warn("%s: bad traddr string\n", __func__); 286062306a36Sopenharmony_ci return -EINVAL; 286162306a36Sopenharmony_ci} 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_cistatic int 286462306a36Sopenharmony_cinvmet_fc_add_port(struct nvmet_port *port) 286562306a36Sopenharmony_ci{ 286662306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport; 286762306a36Sopenharmony_ci struct nvmet_fc_port_entry *pe; 286862306a36Sopenharmony_ci struct nvmet_fc_traddr traddr = { 0L, 0L }; 286962306a36Sopenharmony_ci unsigned long flags; 287062306a36Sopenharmony_ci int ret; 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci /* validate the address info */ 287362306a36Sopenharmony_ci if ((port->disc_addr.trtype != NVMF_TRTYPE_FC) || 287462306a36Sopenharmony_ci (port->disc_addr.adrfam != NVMF_ADDR_FAMILY_FC)) 287562306a36Sopenharmony_ci return -EINVAL; 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci /* map the traddr address info to a target port */ 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_ci ret = nvme_fc_parse_traddr(&traddr, port->disc_addr.traddr, 288062306a36Sopenharmony_ci sizeof(port->disc_addr.traddr)); 288162306a36Sopenharmony_ci if (ret) 288262306a36Sopenharmony_ci return ret; 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci pe = kzalloc(sizeof(*pe), GFP_KERNEL); 288562306a36Sopenharmony_ci if (!pe) 288662306a36Sopenharmony_ci return -ENOMEM; 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci ret = -ENXIO; 288962306a36Sopenharmony_ci spin_lock_irqsave(&nvmet_fc_tgtlock, flags); 289062306a36Sopenharmony_ci list_for_each_entry(tgtport, &nvmet_fc_target_list, tgt_list) { 289162306a36Sopenharmony_ci if ((tgtport->fc_target_port.node_name == traddr.nn) && 289262306a36Sopenharmony_ci (tgtport->fc_target_port.port_name == traddr.pn)) { 289362306a36Sopenharmony_ci /* a FC port can only be 1 nvmet port id */ 289462306a36Sopenharmony_ci if (!tgtport->pe) { 289562306a36Sopenharmony_ci nvmet_fc_portentry_bind(tgtport, pe, port); 289662306a36Sopenharmony_ci ret = 0; 289762306a36Sopenharmony_ci } else 289862306a36Sopenharmony_ci ret = -EALREADY; 289962306a36Sopenharmony_ci break; 290062306a36Sopenharmony_ci } 290162306a36Sopenharmony_ci } 290262306a36Sopenharmony_ci spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags); 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci if (ret) 290562306a36Sopenharmony_ci kfree(pe); 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci return ret; 290862306a36Sopenharmony_ci} 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_cistatic void 291162306a36Sopenharmony_cinvmet_fc_remove_port(struct nvmet_port *port) 291262306a36Sopenharmony_ci{ 291362306a36Sopenharmony_ci struct nvmet_fc_port_entry *pe = port->priv; 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci nvmet_fc_portentry_unbind(pe); 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci /* terminate any outstanding associations */ 291862306a36Sopenharmony_ci __nvmet_fc_free_assocs(pe->tgtport); 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci kfree(pe); 292162306a36Sopenharmony_ci} 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_cistatic void 292462306a36Sopenharmony_cinvmet_fc_discovery_chg(struct nvmet_port *port) 292562306a36Sopenharmony_ci{ 292662306a36Sopenharmony_ci struct nvmet_fc_port_entry *pe = port->priv; 292762306a36Sopenharmony_ci struct nvmet_fc_tgtport *tgtport = pe->tgtport; 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci if (tgtport && tgtport->ops->discovery_event) 293062306a36Sopenharmony_ci tgtport->ops->discovery_event(&tgtport->fc_target_port); 293162306a36Sopenharmony_ci} 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_cistatic const struct nvmet_fabrics_ops nvmet_fc_tgt_fcp_ops = { 293462306a36Sopenharmony_ci .owner = THIS_MODULE, 293562306a36Sopenharmony_ci .type = NVMF_TRTYPE_FC, 293662306a36Sopenharmony_ci .msdbd = 1, 293762306a36Sopenharmony_ci .add_port = nvmet_fc_add_port, 293862306a36Sopenharmony_ci .remove_port = nvmet_fc_remove_port, 293962306a36Sopenharmony_ci .queue_response = nvmet_fc_fcp_nvme_cmd_done, 294062306a36Sopenharmony_ci .delete_ctrl = nvmet_fc_delete_ctrl, 294162306a36Sopenharmony_ci .discovery_chg = nvmet_fc_discovery_chg, 294262306a36Sopenharmony_ci}; 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_cistatic int __init nvmet_fc_init_module(void) 294562306a36Sopenharmony_ci{ 294662306a36Sopenharmony_ci return nvmet_register_transport(&nvmet_fc_tgt_fcp_ops); 294762306a36Sopenharmony_ci} 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_cistatic void __exit nvmet_fc_exit_module(void) 295062306a36Sopenharmony_ci{ 295162306a36Sopenharmony_ci /* ensure any shutdown operation, e.g. delete ctrls have finished */ 295262306a36Sopenharmony_ci flush_workqueue(nvmet_wq); 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci /* sanity check - all lports should be removed */ 295562306a36Sopenharmony_ci if (!list_empty(&nvmet_fc_target_list)) 295662306a36Sopenharmony_ci pr_warn("%s: targetport list not empty\n", __func__); 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci nvmet_unregister_transport(&nvmet_fc_tgt_fcp_ops); 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci ida_destroy(&nvmet_fc_tgtport_cnt); 296162306a36Sopenharmony_ci} 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_cimodule_init(nvmet_fc_init_module); 296462306a36Sopenharmony_cimodule_exit(nvmet_fc_exit_module); 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2967