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/parser.h> 862306a36Sopenharmony_ci#include <uapi/scsi/fc/fc_fs.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "../host/nvme.h" 1162306a36Sopenharmony_ci#include "../target/nvmet.h" 1262306a36Sopenharmony_ci#include <linux/nvme-fc-driver.h> 1362306a36Sopenharmony_ci#include <linux/nvme-fc.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cienum { 1762306a36Sopenharmony_ci NVMF_OPT_ERR = 0, 1862306a36Sopenharmony_ci NVMF_OPT_WWNN = 1 << 0, 1962306a36Sopenharmony_ci NVMF_OPT_WWPN = 1 << 1, 2062306a36Sopenharmony_ci NVMF_OPT_ROLES = 1 << 2, 2162306a36Sopenharmony_ci NVMF_OPT_FCADDR = 1 << 3, 2262306a36Sopenharmony_ci NVMF_OPT_LPWWNN = 1 << 4, 2362306a36Sopenharmony_ci NVMF_OPT_LPWWPN = 1 << 5, 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct fcloop_ctrl_options { 2762306a36Sopenharmony_ci int mask; 2862306a36Sopenharmony_ci u64 wwnn; 2962306a36Sopenharmony_ci u64 wwpn; 3062306a36Sopenharmony_ci u32 roles; 3162306a36Sopenharmony_ci u32 fcaddr; 3262306a36Sopenharmony_ci u64 lpwwnn; 3362306a36Sopenharmony_ci u64 lpwwpn; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const match_table_t opt_tokens = { 3762306a36Sopenharmony_ci { NVMF_OPT_WWNN, "wwnn=%s" }, 3862306a36Sopenharmony_ci { NVMF_OPT_WWPN, "wwpn=%s" }, 3962306a36Sopenharmony_ci { NVMF_OPT_ROLES, "roles=%d" }, 4062306a36Sopenharmony_ci { NVMF_OPT_FCADDR, "fcaddr=%x" }, 4162306a36Sopenharmony_ci { NVMF_OPT_LPWWNN, "lpwwnn=%s" }, 4262306a36Sopenharmony_ci { NVMF_OPT_LPWWPN, "lpwwpn=%s" }, 4362306a36Sopenharmony_ci { NVMF_OPT_ERR, NULL } 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int fcloop_verify_addr(substring_t *s) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci size_t blen = s->to - s->from + 1; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (strnlen(s->from, blen) != NVME_FC_TRADDR_HEXNAMELEN + 2 || 5162306a36Sopenharmony_ci strncmp(s->from, "0x", 2)) 5262306a36Sopenharmony_ci return -EINVAL; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int 5862306a36Sopenharmony_cifcloop_parse_options(struct fcloop_ctrl_options *opts, 5962306a36Sopenharmony_ci const char *buf) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 6262306a36Sopenharmony_ci char *options, *o, *p; 6362306a36Sopenharmony_ci int token, ret = 0; 6462306a36Sopenharmony_ci u64 token64; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci options = o = kstrdup(buf, GFP_KERNEL); 6762306a36Sopenharmony_ci if (!options) 6862306a36Sopenharmony_ci return -ENOMEM; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci while ((p = strsep(&o, ",\n")) != NULL) { 7162306a36Sopenharmony_ci if (!*p) 7262306a36Sopenharmony_ci continue; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci token = match_token(p, opt_tokens, args); 7562306a36Sopenharmony_ci opts->mask |= token; 7662306a36Sopenharmony_ci switch (token) { 7762306a36Sopenharmony_ci case NVMF_OPT_WWNN: 7862306a36Sopenharmony_ci if (fcloop_verify_addr(args) || 7962306a36Sopenharmony_ci match_u64(args, &token64)) { 8062306a36Sopenharmony_ci ret = -EINVAL; 8162306a36Sopenharmony_ci goto out_free_options; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci opts->wwnn = token64; 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case NVMF_OPT_WWPN: 8662306a36Sopenharmony_ci if (fcloop_verify_addr(args) || 8762306a36Sopenharmony_ci match_u64(args, &token64)) { 8862306a36Sopenharmony_ci ret = -EINVAL; 8962306a36Sopenharmony_ci goto out_free_options; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci opts->wwpn = token64; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case NVMF_OPT_ROLES: 9462306a36Sopenharmony_ci if (match_int(args, &token)) { 9562306a36Sopenharmony_ci ret = -EINVAL; 9662306a36Sopenharmony_ci goto out_free_options; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci opts->roles = token; 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci case NVMF_OPT_FCADDR: 10162306a36Sopenharmony_ci if (match_hex(args, &token)) { 10262306a36Sopenharmony_ci ret = -EINVAL; 10362306a36Sopenharmony_ci goto out_free_options; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci opts->fcaddr = token; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case NVMF_OPT_LPWWNN: 10862306a36Sopenharmony_ci if (fcloop_verify_addr(args) || 10962306a36Sopenharmony_ci match_u64(args, &token64)) { 11062306a36Sopenharmony_ci ret = -EINVAL; 11162306a36Sopenharmony_ci goto out_free_options; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci opts->lpwwnn = token64; 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case NVMF_OPT_LPWWPN: 11662306a36Sopenharmony_ci if (fcloop_verify_addr(args) || 11762306a36Sopenharmony_ci match_u64(args, &token64)) { 11862306a36Sopenharmony_ci ret = -EINVAL; 11962306a36Sopenharmony_ci goto out_free_options; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci opts->lpwwpn = token64; 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci default: 12462306a36Sopenharmony_ci pr_warn("unknown parameter or missing value '%s'\n", p); 12562306a36Sopenharmony_ci ret = -EINVAL; 12662306a36Sopenharmony_ci goto out_free_options; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciout_free_options: 13162306a36Sopenharmony_ci kfree(options); 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int 13762306a36Sopenharmony_cifcloop_parse_nm_options(struct device *dev, u64 *nname, u64 *pname, 13862306a36Sopenharmony_ci const char *buf) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 14162306a36Sopenharmony_ci char *options, *o, *p; 14262306a36Sopenharmony_ci int token, ret = 0; 14362306a36Sopenharmony_ci u64 token64; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci *nname = -1; 14662306a36Sopenharmony_ci *pname = -1; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci options = o = kstrdup(buf, GFP_KERNEL); 14962306a36Sopenharmony_ci if (!options) 15062306a36Sopenharmony_ci return -ENOMEM; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci while ((p = strsep(&o, ",\n")) != NULL) { 15362306a36Sopenharmony_ci if (!*p) 15462306a36Sopenharmony_ci continue; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci token = match_token(p, opt_tokens, args); 15762306a36Sopenharmony_ci switch (token) { 15862306a36Sopenharmony_ci case NVMF_OPT_WWNN: 15962306a36Sopenharmony_ci if (fcloop_verify_addr(args) || 16062306a36Sopenharmony_ci match_u64(args, &token64)) { 16162306a36Sopenharmony_ci ret = -EINVAL; 16262306a36Sopenharmony_ci goto out_free_options; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci *nname = token64; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci case NVMF_OPT_WWPN: 16762306a36Sopenharmony_ci if (fcloop_verify_addr(args) || 16862306a36Sopenharmony_ci match_u64(args, &token64)) { 16962306a36Sopenharmony_ci ret = -EINVAL; 17062306a36Sopenharmony_ci goto out_free_options; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci *pname = token64; 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci default: 17562306a36Sopenharmony_ci pr_warn("unknown parameter or missing value '%s'\n", p); 17662306a36Sopenharmony_ci ret = -EINVAL; 17762306a36Sopenharmony_ci goto out_free_options; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciout_free_options: 18262306a36Sopenharmony_ci kfree(options); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!ret) { 18562306a36Sopenharmony_ci if (*nname == -1) 18662306a36Sopenharmony_ci return -EINVAL; 18762306a36Sopenharmony_ci if (*pname == -1) 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#define LPORT_OPTS (NVMF_OPT_WWNN | NVMF_OPT_WWPN) 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#define RPORT_OPTS (NVMF_OPT_WWNN | NVMF_OPT_WWPN | \ 19862306a36Sopenharmony_ci NVMF_OPT_LPWWNN | NVMF_OPT_LPWWPN) 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci#define TGTPORT_OPTS (NVMF_OPT_WWNN | NVMF_OPT_WWPN) 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(fcloop_lock); 20462306a36Sopenharmony_cistatic LIST_HEAD(fcloop_lports); 20562306a36Sopenharmony_cistatic LIST_HEAD(fcloop_nports); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistruct fcloop_lport { 20862306a36Sopenharmony_ci struct nvme_fc_local_port *localport; 20962306a36Sopenharmony_ci struct list_head lport_list; 21062306a36Sopenharmony_ci struct completion unreg_done; 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistruct fcloop_lport_priv { 21462306a36Sopenharmony_ci struct fcloop_lport *lport; 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistruct fcloop_rport { 21862306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport; 21962306a36Sopenharmony_ci struct nvmet_fc_target_port *targetport; 22062306a36Sopenharmony_ci struct fcloop_nport *nport; 22162306a36Sopenharmony_ci struct fcloop_lport *lport; 22262306a36Sopenharmony_ci spinlock_t lock; 22362306a36Sopenharmony_ci struct list_head ls_list; 22462306a36Sopenharmony_ci struct work_struct ls_work; 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistruct fcloop_tport { 22862306a36Sopenharmony_ci struct nvmet_fc_target_port *targetport; 22962306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport; 23062306a36Sopenharmony_ci struct fcloop_nport *nport; 23162306a36Sopenharmony_ci struct fcloop_lport *lport; 23262306a36Sopenharmony_ci spinlock_t lock; 23362306a36Sopenharmony_ci struct list_head ls_list; 23462306a36Sopenharmony_ci struct work_struct ls_work; 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistruct fcloop_nport { 23862306a36Sopenharmony_ci struct fcloop_rport *rport; 23962306a36Sopenharmony_ci struct fcloop_tport *tport; 24062306a36Sopenharmony_ci struct fcloop_lport *lport; 24162306a36Sopenharmony_ci struct list_head nport_list; 24262306a36Sopenharmony_ci struct kref ref; 24362306a36Sopenharmony_ci u64 node_name; 24462306a36Sopenharmony_ci u64 port_name; 24562306a36Sopenharmony_ci u32 port_role; 24662306a36Sopenharmony_ci u32 port_id; 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistruct fcloop_lsreq { 25062306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq; 25162306a36Sopenharmony_ci struct nvmefc_ls_rsp ls_rsp; 25262306a36Sopenharmony_ci int lsdir; /* H2T or T2H */ 25362306a36Sopenharmony_ci int status; 25462306a36Sopenharmony_ci struct list_head ls_list; /* fcloop_rport->ls_list */ 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistruct fcloop_rscn { 25862306a36Sopenharmony_ci struct fcloop_tport *tport; 25962306a36Sopenharmony_ci struct work_struct work; 26062306a36Sopenharmony_ci}; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cienum { 26362306a36Sopenharmony_ci INI_IO_START = 0, 26462306a36Sopenharmony_ci INI_IO_ACTIVE = 1, 26562306a36Sopenharmony_ci INI_IO_ABORTED = 2, 26662306a36Sopenharmony_ci INI_IO_COMPLETED = 3, 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistruct fcloop_fcpreq { 27062306a36Sopenharmony_ci struct fcloop_tport *tport; 27162306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq; 27262306a36Sopenharmony_ci spinlock_t reqlock; 27362306a36Sopenharmony_ci u16 status; 27462306a36Sopenharmony_ci u32 inistate; 27562306a36Sopenharmony_ci bool active; 27662306a36Sopenharmony_ci bool aborted; 27762306a36Sopenharmony_ci struct kref ref; 27862306a36Sopenharmony_ci struct work_struct fcp_rcv_work; 27962306a36Sopenharmony_ci struct work_struct abort_rcv_work; 28062306a36Sopenharmony_ci struct work_struct tio_done_work; 28162306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req tgt_fcp_req; 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistruct fcloop_ini_fcpreq { 28562306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq; 28662306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req; 28762306a36Sopenharmony_ci spinlock_t inilock; 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic inline struct fcloop_lsreq * 29162306a36Sopenharmony_cils_rsp_to_lsreq(struct nvmefc_ls_rsp *lsrsp) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci return container_of(lsrsp, struct fcloop_lsreq, ls_rsp); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic inline struct fcloop_fcpreq * 29762306a36Sopenharmony_citgt_fcp_req_to_fcpreq(struct nvmefc_tgt_fcp_req *tgt_fcpreq) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci return container_of(tgt_fcpreq, struct fcloop_fcpreq, tgt_fcp_req); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int 30462306a36Sopenharmony_cifcloop_create_queue(struct nvme_fc_local_port *localport, 30562306a36Sopenharmony_ci unsigned int qidx, u16 qsize, 30662306a36Sopenharmony_ci void **handle) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci *handle = localport; 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void 31362306a36Sopenharmony_cifcloop_delete_queue(struct nvme_fc_local_port *localport, 31462306a36Sopenharmony_ci unsigned int idx, void *handle) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void 31962306a36Sopenharmony_cifcloop_rport_lsrqst_work(struct work_struct *work) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct fcloop_rport *rport = 32262306a36Sopenharmony_ci container_of(work, struct fcloop_rport, ls_work); 32362306a36Sopenharmony_ci struct fcloop_lsreq *tls_req; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci spin_lock(&rport->lock); 32662306a36Sopenharmony_ci for (;;) { 32762306a36Sopenharmony_ci tls_req = list_first_entry_or_null(&rport->ls_list, 32862306a36Sopenharmony_ci struct fcloop_lsreq, ls_list); 32962306a36Sopenharmony_ci if (!tls_req) 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci list_del(&tls_req->ls_list); 33362306a36Sopenharmony_ci spin_unlock(&rport->lock); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci tls_req->lsreq->done(tls_req->lsreq, tls_req->status); 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * callee may free memory containing tls_req. 33862306a36Sopenharmony_ci * do not reference lsreq after this. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci spin_lock(&rport->lock); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci spin_unlock(&rport->lock); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int 34762306a36Sopenharmony_cifcloop_h2t_ls_req(struct nvme_fc_local_port *localport, 34862306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport, 34962306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct fcloop_lsreq *tls_req = lsreq->private; 35262306a36Sopenharmony_ci struct fcloop_rport *rport = remoteport->private; 35362306a36Sopenharmony_ci int ret = 0; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci tls_req->lsreq = lsreq; 35662306a36Sopenharmony_ci INIT_LIST_HEAD(&tls_req->ls_list); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!rport->targetport) { 35962306a36Sopenharmony_ci tls_req->status = -ECONNREFUSED; 36062306a36Sopenharmony_ci spin_lock(&rport->lock); 36162306a36Sopenharmony_ci list_add_tail(&tls_req->ls_list, &rport->ls_list); 36262306a36Sopenharmony_ci spin_unlock(&rport->lock); 36362306a36Sopenharmony_ci queue_work(nvmet_wq, &rport->ls_work); 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci tls_req->status = 0; 36862306a36Sopenharmony_ci ret = nvmet_fc_rcv_ls_req(rport->targetport, rport, 36962306a36Sopenharmony_ci &tls_req->ls_rsp, 37062306a36Sopenharmony_ci lsreq->rqstaddr, lsreq->rqstlen); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int 37662306a36Sopenharmony_cifcloop_h2t_xmt_ls_rsp(struct nvmet_fc_target_port *targetport, 37762306a36Sopenharmony_ci struct nvmefc_ls_rsp *lsrsp) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct fcloop_lsreq *tls_req = ls_rsp_to_lsreq(lsrsp); 38062306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq = tls_req->lsreq; 38162306a36Sopenharmony_ci struct fcloop_tport *tport = targetport->private; 38262306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport = tport->remoteport; 38362306a36Sopenharmony_ci struct fcloop_rport *rport; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci memcpy(lsreq->rspaddr, lsrsp->rspbuf, 38662306a36Sopenharmony_ci ((lsreq->rsplen < lsrsp->rsplen) ? 38762306a36Sopenharmony_ci lsreq->rsplen : lsrsp->rsplen)); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci lsrsp->done(lsrsp); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (remoteport) { 39262306a36Sopenharmony_ci rport = remoteport->private; 39362306a36Sopenharmony_ci spin_lock(&rport->lock); 39462306a36Sopenharmony_ci list_add_tail(&tls_req->ls_list, &rport->ls_list); 39562306a36Sopenharmony_ci spin_unlock(&rport->lock); 39662306a36Sopenharmony_ci queue_work(nvmet_wq, &rport->ls_work); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void 40362306a36Sopenharmony_cifcloop_tport_lsrqst_work(struct work_struct *work) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct fcloop_tport *tport = 40662306a36Sopenharmony_ci container_of(work, struct fcloop_tport, ls_work); 40762306a36Sopenharmony_ci struct fcloop_lsreq *tls_req; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci spin_lock(&tport->lock); 41062306a36Sopenharmony_ci for (;;) { 41162306a36Sopenharmony_ci tls_req = list_first_entry_or_null(&tport->ls_list, 41262306a36Sopenharmony_ci struct fcloop_lsreq, ls_list); 41362306a36Sopenharmony_ci if (!tls_req) 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci list_del(&tls_req->ls_list); 41762306a36Sopenharmony_ci spin_unlock(&tport->lock); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci tls_req->lsreq->done(tls_req->lsreq, tls_req->status); 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * callee may free memory containing tls_req. 42262306a36Sopenharmony_ci * do not reference lsreq after this. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci spin_lock(&tport->lock); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci spin_unlock(&tport->lock); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int 43162306a36Sopenharmony_cifcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle, 43262306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct fcloop_lsreq *tls_req = lsreq->private; 43562306a36Sopenharmony_ci struct fcloop_tport *tport = targetport->private; 43662306a36Sopenharmony_ci int ret = 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* 43962306a36Sopenharmony_ci * hosthandle should be the dst.rport value. 44062306a36Sopenharmony_ci * hosthandle ignored as fcloop currently is 44162306a36Sopenharmony_ci * 1:1 tgtport vs remoteport 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ci tls_req->lsreq = lsreq; 44462306a36Sopenharmony_ci INIT_LIST_HEAD(&tls_req->ls_list); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!tport->remoteport) { 44762306a36Sopenharmony_ci tls_req->status = -ECONNREFUSED; 44862306a36Sopenharmony_ci spin_lock(&tport->lock); 44962306a36Sopenharmony_ci list_add_tail(&tls_req->ls_list, &tport->ls_list); 45062306a36Sopenharmony_ci spin_unlock(&tport->lock); 45162306a36Sopenharmony_ci queue_work(nvmet_wq, &tport->ls_work); 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci tls_req->status = 0; 45662306a36Sopenharmony_ci ret = nvme_fc_rcv_ls_req(tport->remoteport, &tls_req->ls_rsp, 45762306a36Sopenharmony_ci lsreq->rqstaddr, lsreq->rqstlen); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return ret; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic int 46362306a36Sopenharmony_cifcloop_t2h_xmt_ls_rsp(struct nvme_fc_local_port *localport, 46462306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport, 46562306a36Sopenharmony_ci struct nvmefc_ls_rsp *lsrsp) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct fcloop_lsreq *tls_req = ls_rsp_to_lsreq(lsrsp); 46862306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq = tls_req->lsreq; 46962306a36Sopenharmony_ci struct fcloop_rport *rport = remoteport->private; 47062306a36Sopenharmony_ci struct nvmet_fc_target_port *targetport = rport->targetport; 47162306a36Sopenharmony_ci struct fcloop_tport *tport; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci memcpy(lsreq->rspaddr, lsrsp->rspbuf, 47462306a36Sopenharmony_ci ((lsreq->rsplen < lsrsp->rsplen) ? 47562306a36Sopenharmony_ci lsreq->rsplen : lsrsp->rsplen)); 47662306a36Sopenharmony_ci lsrsp->done(lsrsp); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (targetport) { 47962306a36Sopenharmony_ci tport = targetport->private; 48062306a36Sopenharmony_ci spin_lock(&tport->lock); 48162306a36Sopenharmony_ci list_add_tail(&tport->ls_list, &tls_req->ls_list); 48262306a36Sopenharmony_ci spin_unlock(&tport->lock); 48362306a36Sopenharmony_ci queue_work(nvmet_wq, &tport->ls_work); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void 49062306a36Sopenharmony_cifcloop_t2h_host_release(void *hosthandle) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci /* host handle ignored for now */ 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci/* 49662306a36Sopenharmony_ci * Simulate reception of RSCN and converting it to a initiator transport 49762306a36Sopenharmony_ci * call to rescan a remote port. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_cistatic void 50062306a36Sopenharmony_cifcloop_tgt_rscn_work(struct work_struct *work) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct fcloop_rscn *tgt_rscn = 50362306a36Sopenharmony_ci container_of(work, struct fcloop_rscn, work); 50462306a36Sopenharmony_ci struct fcloop_tport *tport = tgt_rscn->tport; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (tport->remoteport) 50762306a36Sopenharmony_ci nvme_fc_rescan_remoteport(tport->remoteport); 50862306a36Sopenharmony_ci kfree(tgt_rscn); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic void 51262306a36Sopenharmony_cifcloop_tgt_discovery_evt(struct nvmet_fc_target_port *tgtport) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct fcloop_rscn *tgt_rscn; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci tgt_rscn = kzalloc(sizeof(*tgt_rscn), GFP_KERNEL); 51762306a36Sopenharmony_ci if (!tgt_rscn) 51862306a36Sopenharmony_ci return; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci tgt_rscn->tport = tgtport->private; 52162306a36Sopenharmony_ci INIT_WORK(&tgt_rscn->work, fcloop_tgt_rscn_work); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci queue_work(nvmet_wq, &tgt_rscn->work); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic void 52762306a36Sopenharmony_cifcloop_tfcp_req_free(struct kref *ref) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req = 53062306a36Sopenharmony_ci container_of(ref, struct fcloop_fcpreq, ref); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci kfree(tfcp_req); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void 53662306a36Sopenharmony_cifcloop_tfcp_req_put(struct fcloop_fcpreq *tfcp_req) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci kref_put(&tfcp_req->ref, fcloop_tfcp_req_free); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int 54262306a36Sopenharmony_cifcloop_tfcp_req_get(struct fcloop_fcpreq *tfcp_req) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci return kref_get_unless_zero(&tfcp_req->ref); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic void 54862306a36Sopenharmony_cifcloop_call_host_done(struct nvmefc_fcp_req *fcpreq, 54962306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req, int status) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct fcloop_ini_fcpreq *inireq = NULL; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (fcpreq) { 55462306a36Sopenharmony_ci inireq = fcpreq->private; 55562306a36Sopenharmony_ci spin_lock(&inireq->inilock); 55662306a36Sopenharmony_ci inireq->tfcp_req = NULL; 55762306a36Sopenharmony_ci spin_unlock(&inireq->inilock); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci fcpreq->status = status; 56062306a36Sopenharmony_ci fcpreq->done(fcpreq); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* release original io reference on tgt struct */ 56462306a36Sopenharmony_ci fcloop_tfcp_req_put(tfcp_req); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic bool drop_fabric_opcode; 56862306a36Sopenharmony_ci#define DROP_OPCODE_MASK 0x00FF 56962306a36Sopenharmony_ci/* fabrics opcode will have a bit set above 1st byte */ 57062306a36Sopenharmony_cistatic int drop_opcode = -1; 57162306a36Sopenharmony_cistatic int drop_instance; 57262306a36Sopenharmony_cistatic int drop_amount; 57362306a36Sopenharmony_cistatic int drop_current_cnt; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/* 57662306a36Sopenharmony_ci * Routine to parse io and determine if the io is to be dropped. 57762306a36Sopenharmony_ci * Returns: 57862306a36Sopenharmony_ci * 0 if io is not obstructed 57962306a36Sopenharmony_ci * 1 if io was dropped 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_cistatic int check_for_drop(struct fcloop_fcpreq *tfcp_req) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq; 58462306a36Sopenharmony_ci struct nvme_fc_cmd_iu *cmdiu = fcpreq->cmdaddr; 58562306a36Sopenharmony_ci struct nvme_command *sqe = &cmdiu->sqe; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (drop_opcode == -1) 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci pr_info("%s: seq opcd x%02x fctype x%02x: drop F %s op x%02x " 59162306a36Sopenharmony_ci "inst %d start %d amt %d\n", 59262306a36Sopenharmony_ci __func__, sqe->common.opcode, sqe->fabrics.fctype, 59362306a36Sopenharmony_ci drop_fabric_opcode ? "y" : "n", 59462306a36Sopenharmony_ci drop_opcode, drop_current_cnt, drop_instance, drop_amount); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if ((drop_fabric_opcode && 59762306a36Sopenharmony_ci (sqe->common.opcode != nvme_fabrics_command || 59862306a36Sopenharmony_ci sqe->fabrics.fctype != drop_opcode)) || 59962306a36Sopenharmony_ci (!drop_fabric_opcode && sqe->common.opcode != drop_opcode)) 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (++drop_current_cnt >= drop_instance) { 60362306a36Sopenharmony_ci if (drop_current_cnt >= drop_instance + drop_amount) 60462306a36Sopenharmony_ci drop_opcode = -1; 60562306a36Sopenharmony_ci return 1; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic void 61262306a36Sopenharmony_cifcloop_fcp_recv_work(struct work_struct *work) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req = 61562306a36Sopenharmony_ci container_of(work, struct fcloop_fcpreq, fcp_rcv_work); 61662306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq; 61762306a36Sopenharmony_ci unsigned long flags; 61862306a36Sopenharmony_ci int ret = 0; 61962306a36Sopenharmony_ci bool aborted = false; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 62262306a36Sopenharmony_ci switch (tfcp_req->inistate) { 62362306a36Sopenharmony_ci case INI_IO_START: 62462306a36Sopenharmony_ci tfcp_req->inistate = INI_IO_ACTIVE; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case INI_IO_ABORTED: 62762306a36Sopenharmony_ci aborted = true; 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci default: 63062306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 63162306a36Sopenharmony_ci WARN_ON(1); 63262306a36Sopenharmony_ci return; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (unlikely(aborted)) 63762306a36Sopenharmony_ci ret = -ECANCELED; 63862306a36Sopenharmony_ci else { 63962306a36Sopenharmony_ci if (likely(!check_for_drop(tfcp_req))) 64062306a36Sopenharmony_ci ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport, 64162306a36Sopenharmony_ci &tfcp_req->tgt_fcp_req, 64262306a36Sopenharmony_ci fcpreq->cmdaddr, fcpreq->cmdlen); 64362306a36Sopenharmony_ci else 64462306a36Sopenharmony_ci pr_info("%s: dropped command ********\n", __func__); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci if (ret) 64762306a36Sopenharmony_ci fcloop_call_host_done(fcpreq, tfcp_req, ret); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void 65162306a36Sopenharmony_cifcloop_fcp_abort_recv_work(struct work_struct *work) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req = 65462306a36Sopenharmony_ci container_of(work, struct fcloop_fcpreq, abort_rcv_work); 65562306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq; 65662306a36Sopenharmony_ci bool completed = false; 65762306a36Sopenharmony_ci unsigned long flags; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 66062306a36Sopenharmony_ci fcpreq = tfcp_req->fcpreq; 66162306a36Sopenharmony_ci switch (tfcp_req->inistate) { 66262306a36Sopenharmony_ci case INI_IO_ABORTED: 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci case INI_IO_COMPLETED: 66562306a36Sopenharmony_ci completed = true; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci default: 66862306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 66962306a36Sopenharmony_ci WARN_ON(1); 67062306a36Sopenharmony_ci return; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (unlikely(completed)) { 67562306a36Sopenharmony_ci /* remove reference taken in original abort downcall */ 67662306a36Sopenharmony_ci fcloop_tfcp_req_put(tfcp_req); 67762306a36Sopenharmony_ci return; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (tfcp_req->tport->targetport) 68162306a36Sopenharmony_ci nvmet_fc_rcv_fcp_abort(tfcp_req->tport->targetport, 68262306a36Sopenharmony_ci &tfcp_req->tgt_fcp_req); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 68562306a36Sopenharmony_ci tfcp_req->fcpreq = NULL; 68662306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci fcloop_call_host_done(fcpreq, tfcp_req, -ECANCELED); 68962306a36Sopenharmony_ci /* call_host_done releases reference for abort downcall */ 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/* 69362306a36Sopenharmony_ci * FCP IO operation done by target completion. 69462306a36Sopenharmony_ci * call back up initiator "done" flows. 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_cistatic void 69762306a36Sopenharmony_cifcloop_tgt_fcprqst_done_work(struct work_struct *work) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req = 70062306a36Sopenharmony_ci container_of(work, struct fcloop_fcpreq, tio_done_work); 70162306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq; 70262306a36Sopenharmony_ci unsigned long flags; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 70562306a36Sopenharmony_ci fcpreq = tfcp_req->fcpreq; 70662306a36Sopenharmony_ci tfcp_req->inistate = INI_IO_COMPLETED; 70762306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci fcloop_call_host_done(fcpreq, tfcp_req, tfcp_req->status); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int 71462306a36Sopenharmony_cifcloop_fcp_req(struct nvme_fc_local_port *localport, 71562306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport, 71662306a36Sopenharmony_ci void *hw_queue_handle, 71762306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct fcloop_rport *rport = remoteport->private; 72062306a36Sopenharmony_ci struct fcloop_ini_fcpreq *inireq = fcpreq->private; 72162306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (!rport->targetport) 72462306a36Sopenharmony_ci return -ECONNREFUSED; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci tfcp_req = kzalloc(sizeof(*tfcp_req), GFP_ATOMIC); 72762306a36Sopenharmony_ci if (!tfcp_req) 72862306a36Sopenharmony_ci return -ENOMEM; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci inireq->fcpreq = fcpreq; 73162306a36Sopenharmony_ci inireq->tfcp_req = tfcp_req; 73262306a36Sopenharmony_ci spin_lock_init(&inireq->inilock); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci tfcp_req->fcpreq = fcpreq; 73562306a36Sopenharmony_ci tfcp_req->tport = rport->targetport->private; 73662306a36Sopenharmony_ci tfcp_req->inistate = INI_IO_START; 73762306a36Sopenharmony_ci spin_lock_init(&tfcp_req->reqlock); 73862306a36Sopenharmony_ci INIT_WORK(&tfcp_req->fcp_rcv_work, fcloop_fcp_recv_work); 73962306a36Sopenharmony_ci INIT_WORK(&tfcp_req->abort_rcv_work, fcloop_fcp_abort_recv_work); 74062306a36Sopenharmony_ci INIT_WORK(&tfcp_req->tio_done_work, fcloop_tgt_fcprqst_done_work); 74162306a36Sopenharmony_ci kref_init(&tfcp_req->ref); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci queue_work(nvmet_wq, &tfcp_req->fcp_rcv_work); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic void 74962306a36Sopenharmony_cifcloop_fcp_copy_data(u8 op, struct scatterlist *data_sg, 75062306a36Sopenharmony_ci struct scatterlist *io_sg, u32 offset, u32 length) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci void *data_p, *io_p; 75362306a36Sopenharmony_ci u32 data_len, io_len, tlen; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci io_p = sg_virt(io_sg); 75662306a36Sopenharmony_ci io_len = io_sg->length; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci for ( ; offset; ) { 75962306a36Sopenharmony_ci tlen = min_t(u32, offset, io_len); 76062306a36Sopenharmony_ci offset -= tlen; 76162306a36Sopenharmony_ci io_len -= tlen; 76262306a36Sopenharmony_ci if (!io_len) { 76362306a36Sopenharmony_ci io_sg = sg_next(io_sg); 76462306a36Sopenharmony_ci io_p = sg_virt(io_sg); 76562306a36Sopenharmony_ci io_len = io_sg->length; 76662306a36Sopenharmony_ci } else 76762306a36Sopenharmony_ci io_p += tlen; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci data_p = sg_virt(data_sg); 77162306a36Sopenharmony_ci data_len = data_sg->length; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci for ( ; length; ) { 77462306a36Sopenharmony_ci tlen = min_t(u32, io_len, data_len); 77562306a36Sopenharmony_ci tlen = min_t(u32, tlen, length); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (op == NVMET_FCOP_WRITEDATA) 77862306a36Sopenharmony_ci memcpy(data_p, io_p, tlen); 77962306a36Sopenharmony_ci else 78062306a36Sopenharmony_ci memcpy(io_p, data_p, tlen); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci length -= tlen; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci io_len -= tlen; 78562306a36Sopenharmony_ci if ((!io_len) && (length)) { 78662306a36Sopenharmony_ci io_sg = sg_next(io_sg); 78762306a36Sopenharmony_ci io_p = sg_virt(io_sg); 78862306a36Sopenharmony_ci io_len = io_sg->length; 78962306a36Sopenharmony_ci } else 79062306a36Sopenharmony_ci io_p += tlen; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci data_len -= tlen; 79362306a36Sopenharmony_ci if ((!data_len) && (length)) { 79462306a36Sopenharmony_ci data_sg = sg_next(data_sg); 79562306a36Sopenharmony_ci data_p = sg_virt(data_sg); 79662306a36Sopenharmony_ci data_len = data_sg->length; 79762306a36Sopenharmony_ci } else 79862306a36Sopenharmony_ci data_p += tlen; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int 80362306a36Sopenharmony_cifcloop_fcp_op(struct nvmet_fc_target_port *tgtport, 80462306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *tgt_fcpreq) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req = tgt_fcp_req_to_fcpreq(tgt_fcpreq); 80762306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq; 80862306a36Sopenharmony_ci u32 rsplen = 0, xfrlen = 0; 80962306a36Sopenharmony_ci int fcp_err = 0, active, aborted; 81062306a36Sopenharmony_ci u8 op = tgt_fcpreq->op; 81162306a36Sopenharmony_ci unsigned long flags; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 81462306a36Sopenharmony_ci fcpreq = tfcp_req->fcpreq; 81562306a36Sopenharmony_ci active = tfcp_req->active; 81662306a36Sopenharmony_ci aborted = tfcp_req->aborted; 81762306a36Sopenharmony_ci tfcp_req->active = true; 81862306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (unlikely(active)) 82162306a36Sopenharmony_ci /* illegal - call while i/o active */ 82262306a36Sopenharmony_ci return -EALREADY; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (unlikely(aborted)) { 82562306a36Sopenharmony_ci /* target transport has aborted i/o prior */ 82662306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 82762306a36Sopenharmony_ci tfcp_req->active = false; 82862306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 82962306a36Sopenharmony_ci tgt_fcpreq->transferred_length = 0; 83062306a36Sopenharmony_ci tgt_fcpreq->fcp_error = -ECANCELED; 83162306a36Sopenharmony_ci tgt_fcpreq->done(tgt_fcpreq); 83262306a36Sopenharmony_ci return 0; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* 83662306a36Sopenharmony_ci * if fcpreq is NULL, the I/O has been aborted (from 83762306a36Sopenharmony_ci * initiator side). For the target side, act as if all is well 83862306a36Sopenharmony_ci * but don't actually move data. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci switch (op) { 84262306a36Sopenharmony_ci case NVMET_FCOP_WRITEDATA: 84362306a36Sopenharmony_ci xfrlen = tgt_fcpreq->transfer_length; 84462306a36Sopenharmony_ci if (fcpreq) { 84562306a36Sopenharmony_ci fcloop_fcp_copy_data(op, tgt_fcpreq->sg, 84662306a36Sopenharmony_ci fcpreq->first_sgl, tgt_fcpreq->offset, 84762306a36Sopenharmony_ci xfrlen); 84862306a36Sopenharmony_ci fcpreq->transferred_length += xfrlen; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci case NVMET_FCOP_READDATA: 85362306a36Sopenharmony_ci case NVMET_FCOP_READDATA_RSP: 85462306a36Sopenharmony_ci xfrlen = tgt_fcpreq->transfer_length; 85562306a36Sopenharmony_ci if (fcpreq) { 85662306a36Sopenharmony_ci fcloop_fcp_copy_data(op, tgt_fcpreq->sg, 85762306a36Sopenharmony_ci fcpreq->first_sgl, tgt_fcpreq->offset, 85862306a36Sopenharmony_ci xfrlen); 85962306a36Sopenharmony_ci fcpreq->transferred_length += xfrlen; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci if (op == NVMET_FCOP_READDATA) 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* Fall-Thru to RSP handling */ 86562306a36Sopenharmony_ci fallthrough; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci case NVMET_FCOP_RSP: 86862306a36Sopenharmony_ci if (fcpreq) { 86962306a36Sopenharmony_ci rsplen = ((fcpreq->rsplen < tgt_fcpreq->rsplen) ? 87062306a36Sopenharmony_ci fcpreq->rsplen : tgt_fcpreq->rsplen); 87162306a36Sopenharmony_ci memcpy(fcpreq->rspaddr, tgt_fcpreq->rspaddr, rsplen); 87262306a36Sopenharmony_ci if (rsplen < tgt_fcpreq->rsplen) 87362306a36Sopenharmony_ci fcp_err = -E2BIG; 87462306a36Sopenharmony_ci fcpreq->rcv_rsplen = rsplen; 87562306a36Sopenharmony_ci fcpreq->status = 0; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci tfcp_req->status = 0; 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci default: 88162306a36Sopenharmony_ci fcp_err = -EINVAL; 88262306a36Sopenharmony_ci break; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 88662306a36Sopenharmony_ci tfcp_req->active = false; 88762306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci tgt_fcpreq->transferred_length = xfrlen; 89062306a36Sopenharmony_ci tgt_fcpreq->fcp_error = fcp_err; 89162306a36Sopenharmony_ci tgt_fcpreq->done(tgt_fcpreq); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return 0; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic void 89762306a36Sopenharmony_cifcloop_tgt_fcp_abort(struct nvmet_fc_target_port *tgtport, 89862306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *tgt_fcpreq) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req = tgt_fcp_req_to_fcpreq(tgt_fcpreq); 90162306a36Sopenharmony_ci unsigned long flags; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* 90462306a36Sopenharmony_ci * mark aborted only in case there were 2 threads in transport 90562306a36Sopenharmony_ci * (one doing io, other doing abort) and only kills ops posted 90662306a36Sopenharmony_ci * after the abort request 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 90962306a36Sopenharmony_ci tfcp_req->aborted = true; 91062306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci tfcp_req->status = NVME_SC_INTERNAL; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* 91562306a36Sopenharmony_ci * nothing more to do. If io wasn't active, the transport should 91662306a36Sopenharmony_ci * immediately call the req_release. If it was active, the op 91762306a36Sopenharmony_ci * will complete, and the lldd should call req_release. 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic void 92262306a36Sopenharmony_cifcloop_fcp_req_release(struct nvmet_fc_target_port *tgtport, 92362306a36Sopenharmony_ci struct nvmefc_tgt_fcp_req *tgt_fcpreq) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req = tgt_fcp_req_to_fcpreq(tgt_fcpreq); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci queue_work(nvmet_wq, &tfcp_req->tio_done_work); 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic void 93162306a36Sopenharmony_cifcloop_h2t_ls_abort(struct nvme_fc_local_port *localport, 93262306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport, 93362306a36Sopenharmony_ci struct nvmefc_ls_req *lsreq) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_cistatic void 93862306a36Sopenharmony_cifcloop_t2h_ls_abort(struct nvmet_fc_target_port *targetport, 93962306a36Sopenharmony_ci void *hosthandle, struct nvmefc_ls_req *lsreq) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic void 94462306a36Sopenharmony_cifcloop_fcp_abort(struct nvme_fc_local_port *localport, 94562306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport, 94662306a36Sopenharmony_ci void *hw_queue_handle, 94762306a36Sopenharmony_ci struct nvmefc_fcp_req *fcpreq) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci struct fcloop_ini_fcpreq *inireq = fcpreq->private; 95062306a36Sopenharmony_ci struct fcloop_fcpreq *tfcp_req; 95162306a36Sopenharmony_ci bool abortio = true; 95262306a36Sopenharmony_ci unsigned long flags; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci spin_lock(&inireq->inilock); 95562306a36Sopenharmony_ci tfcp_req = inireq->tfcp_req; 95662306a36Sopenharmony_ci if (tfcp_req) 95762306a36Sopenharmony_ci fcloop_tfcp_req_get(tfcp_req); 95862306a36Sopenharmony_ci spin_unlock(&inireq->inilock); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (!tfcp_req) 96162306a36Sopenharmony_ci /* abort has already been called */ 96262306a36Sopenharmony_ci return; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* break initiator/target relationship for io */ 96562306a36Sopenharmony_ci spin_lock_irqsave(&tfcp_req->reqlock, flags); 96662306a36Sopenharmony_ci switch (tfcp_req->inistate) { 96762306a36Sopenharmony_ci case INI_IO_START: 96862306a36Sopenharmony_ci case INI_IO_ACTIVE: 96962306a36Sopenharmony_ci tfcp_req->inistate = INI_IO_ABORTED; 97062306a36Sopenharmony_ci break; 97162306a36Sopenharmony_ci case INI_IO_COMPLETED: 97262306a36Sopenharmony_ci abortio = false; 97362306a36Sopenharmony_ci break; 97462306a36Sopenharmony_ci default: 97562306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 97662306a36Sopenharmony_ci WARN_ON(1); 97762306a36Sopenharmony_ci return; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci spin_unlock_irqrestore(&tfcp_req->reqlock, flags); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (abortio) 98262306a36Sopenharmony_ci /* leave the reference while the work item is scheduled */ 98362306a36Sopenharmony_ci WARN_ON(!queue_work(nvmet_wq, &tfcp_req->abort_rcv_work)); 98462306a36Sopenharmony_ci else { 98562306a36Sopenharmony_ci /* 98662306a36Sopenharmony_ci * as the io has already had the done callback made, 98762306a36Sopenharmony_ci * nothing more to do. So release the reference taken above 98862306a36Sopenharmony_ci */ 98962306a36Sopenharmony_ci fcloop_tfcp_req_put(tfcp_req); 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic void 99462306a36Sopenharmony_cifcloop_nport_free(struct kref *ref) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct fcloop_nport *nport = 99762306a36Sopenharmony_ci container_of(ref, struct fcloop_nport, ref); 99862306a36Sopenharmony_ci unsigned long flags; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 100162306a36Sopenharmony_ci list_del(&nport->nport_list); 100262306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci kfree(nport); 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic void 100862306a36Sopenharmony_cifcloop_nport_put(struct fcloop_nport *nport) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci kref_put(&nport->ref, fcloop_nport_free); 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic int 101462306a36Sopenharmony_cifcloop_nport_get(struct fcloop_nport *nport) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci return kref_get_unless_zero(&nport->ref); 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic void 102062306a36Sopenharmony_cifcloop_localport_delete(struct nvme_fc_local_port *localport) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct fcloop_lport_priv *lport_priv = localport->private; 102362306a36Sopenharmony_ci struct fcloop_lport *lport = lport_priv->lport; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* release any threads waiting for the unreg to complete */ 102662306a36Sopenharmony_ci complete(&lport->unreg_done); 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic void 103062306a36Sopenharmony_cifcloop_remoteport_delete(struct nvme_fc_remote_port *remoteport) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci struct fcloop_rport *rport = remoteport->private; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci flush_work(&rport->ls_work); 103562306a36Sopenharmony_ci fcloop_nport_put(rport->nport); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic void 103962306a36Sopenharmony_cifcloop_targetport_delete(struct nvmet_fc_target_port *targetport) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct fcloop_tport *tport = targetport->private; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci flush_work(&tport->ls_work); 104462306a36Sopenharmony_ci fcloop_nport_put(tport->nport); 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci#define FCLOOP_HW_QUEUES 4 104862306a36Sopenharmony_ci#define FCLOOP_SGL_SEGS 256 104962306a36Sopenharmony_ci#define FCLOOP_DMABOUND_4G 0xFFFFFFFF 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic struct nvme_fc_port_template fctemplate = { 105262306a36Sopenharmony_ci .localport_delete = fcloop_localport_delete, 105362306a36Sopenharmony_ci .remoteport_delete = fcloop_remoteport_delete, 105462306a36Sopenharmony_ci .create_queue = fcloop_create_queue, 105562306a36Sopenharmony_ci .delete_queue = fcloop_delete_queue, 105662306a36Sopenharmony_ci .ls_req = fcloop_h2t_ls_req, 105762306a36Sopenharmony_ci .fcp_io = fcloop_fcp_req, 105862306a36Sopenharmony_ci .ls_abort = fcloop_h2t_ls_abort, 105962306a36Sopenharmony_ci .fcp_abort = fcloop_fcp_abort, 106062306a36Sopenharmony_ci .xmt_ls_rsp = fcloop_t2h_xmt_ls_rsp, 106162306a36Sopenharmony_ci .max_hw_queues = FCLOOP_HW_QUEUES, 106262306a36Sopenharmony_ci .max_sgl_segments = FCLOOP_SGL_SEGS, 106362306a36Sopenharmony_ci .max_dif_sgl_segments = FCLOOP_SGL_SEGS, 106462306a36Sopenharmony_ci .dma_boundary = FCLOOP_DMABOUND_4G, 106562306a36Sopenharmony_ci /* sizes of additional private data for data structures */ 106662306a36Sopenharmony_ci .local_priv_sz = sizeof(struct fcloop_lport_priv), 106762306a36Sopenharmony_ci .remote_priv_sz = sizeof(struct fcloop_rport), 106862306a36Sopenharmony_ci .lsrqst_priv_sz = sizeof(struct fcloop_lsreq), 106962306a36Sopenharmony_ci .fcprqst_priv_sz = sizeof(struct fcloop_ini_fcpreq), 107062306a36Sopenharmony_ci}; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic struct nvmet_fc_target_template tgttemplate = { 107362306a36Sopenharmony_ci .targetport_delete = fcloop_targetport_delete, 107462306a36Sopenharmony_ci .xmt_ls_rsp = fcloop_h2t_xmt_ls_rsp, 107562306a36Sopenharmony_ci .fcp_op = fcloop_fcp_op, 107662306a36Sopenharmony_ci .fcp_abort = fcloop_tgt_fcp_abort, 107762306a36Sopenharmony_ci .fcp_req_release = fcloop_fcp_req_release, 107862306a36Sopenharmony_ci .discovery_event = fcloop_tgt_discovery_evt, 107962306a36Sopenharmony_ci .ls_req = fcloop_t2h_ls_req, 108062306a36Sopenharmony_ci .ls_abort = fcloop_t2h_ls_abort, 108162306a36Sopenharmony_ci .host_release = fcloop_t2h_host_release, 108262306a36Sopenharmony_ci .max_hw_queues = FCLOOP_HW_QUEUES, 108362306a36Sopenharmony_ci .max_sgl_segments = FCLOOP_SGL_SEGS, 108462306a36Sopenharmony_ci .max_dif_sgl_segments = FCLOOP_SGL_SEGS, 108562306a36Sopenharmony_ci .dma_boundary = FCLOOP_DMABOUND_4G, 108662306a36Sopenharmony_ci /* optional features */ 108762306a36Sopenharmony_ci .target_features = 0, 108862306a36Sopenharmony_ci /* sizes of additional private data for data structures */ 108962306a36Sopenharmony_ci .target_priv_sz = sizeof(struct fcloop_tport), 109062306a36Sopenharmony_ci .lsrqst_priv_sz = sizeof(struct fcloop_lsreq), 109162306a36Sopenharmony_ci}; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic ssize_t 109462306a36Sopenharmony_cifcloop_create_local_port(struct device *dev, struct device_attribute *attr, 109562306a36Sopenharmony_ci const char *buf, size_t count) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct nvme_fc_port_info pinfo; 109862306a36Sopenharmony_ci struct fcloop_ctrl_options *opts; 109962306a36Sopenharmony_ci struct nvme_fc_local_port *localport; 110062306a36Sopenharmony_ci struct fcloop_lport *lport; 110162306a36Sopenharmony_ci struct fcloop_lport_priv *lport_priv; 110262306a36Sopenharmony_ci unsigned long flags; 110362306a36Sopenharmony_ci int ret = -ENOMEM; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci lport = kzalloc(sizeof(*lport), GFP_KERNEL); 110662306a36Sopenharmony_ci if (!lport) 110762306a36Sopenharmony_ci return -ENOMEM; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 111062306a36Sopenharmony_ci if (!opts) 111162306a36Sopenharmony_ci goto out_free_lport; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci ret = fcloop_parse_options(opts, buf); 111462306a36Sopenharmony_ci if (ret) 111562306a36Sopenharmony_ci goto out_free_opts; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci /* everything there ? */ 111862306a36Sopenharmony_ci if ((opts->mask & LPORT_OPTS) != LPORT_OPTS) { 111962306a36Sopenharmony_ci ret = -EINVAL; 112062306a36Sopenharmony_ci goto out_free_opts; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci memset(&pinfo, 0, sizeof(pinfo)); 112462306a36Sopenharmony_ci pinfo.node_name = opts->wwnn; 112562306a36Sopenharmony_ci pinfo.port_name = opts->wwpn; 112662306a36Sopenharmony_ci pinfo.port_role = opts->roles; 112762306a36Sopenharmony_ci pinfo.port_id = opts->fcaddr; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci ret = nvme_fc_register_localport(&pinfo, &fctemplate, NULL, &localport); 113062306a36Sopenharmony_ci if (!ret) { 113162306a36Sopenharmony_ci /* success */ 113262306a36Sopenharmony_ci lport_priv = localport->private; 113362306a36Sopenharmony_ci lport_priv->lport = lport; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci lport->localport = localport; 113662306a36Sopenharmony_ci INIT_LIST_HEAD(&lport->lport_list); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 113962306a36Sopenharmony_ci list_add_tail(&lport->lport_list, &fcloop_lports); 114062306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ciout_free_opts: 114462306a36Sopenharmony_ci kfree(opts); 114562306a36Sopenharmony_ciout_free_lport: 114662306a36Sopenharmony_ci /* free only if we're going to fail */ 114762306a36Sopenharmony_ci if (ret) 114862306a36Sopenharmony_ci kfree(lport); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci return ret ? ret : count; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic void 115562306a36Sopenharmony_ci__unlink_local_port(struct fcloop_lport *lport) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci list_del(&lport->lport_list); 115862306a36Sopenharmony_ci} 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_cistatic int 116162306a36Sopenharmony_ci__wait_localport_unreg(struct fcloop_lport *lport) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci int ret; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci init_completion(&lport->unreg_done); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci ret = nvme_fc_unregister_localport(lport->localport); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (!ret) 117062306a36Sopenharmony_ci wait_for_completion(&lport->unreg_done); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci kfree(lport); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci return ret; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic ssize_t 117962306a36Sopenharmony_cifcloop_delete_local_port(struct device *dev, struct device_attribute *attr, 118062306a36Sopenharmony_ci const char *buf, size_t count) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci struct fcloop_lport *tlport, *lport = NULL; 118362306a36Sopenharmony_ci u64 nodename, portname; 118462306a36Sopenharmony_ci unsigned long flags; 118562306a36Sopenharmony_ci int ret; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci ret = fcloop_parse_nm_options(dev, &nodename, &portname, buf); 118862306a36Sopenharmony_ci if (ret) 118962306a36Sopenharmony_ci return ret; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci list_for_each_entry(tlport, &fcloop_lports, lport_list) { 119462306a36Sopenharmony_ci if (tlport->localport->node_name == nodename && 119562306a36Sopenharmony_ci tlport->localport->port_name == portname) { 119662306a36Sopenharmony_ci lport = tlport; 119762306a36Sopenharmony_ci __unlink_local_port(lport); 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (!lport) 120462306a36Sopenharmony_ci return -ENOENT; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci ret = __wait_localport_unreg(lport); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return ret ? ret : count; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic struct fcloop_nport * 121262306a36Sopenharmony_cifcloop_alloc_nport(const char *buf, size_t count, bool remoteport) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci struct fcloop_nport *newnport, *nport = NULL; 121562306a36Sopenharmony_ci struct fcloop_lport *tmplport, *lport = NULL; 121662306a36Sopenharmony_ci struct fcloop_ctrl_options *opts; 121762306a36Sopenharmony_ci unsigned long flags; 121862306a36Sopenharmony_ci u32 opts_mask = (remoteport) ? RPORT_OPTS : TGTPORT_OPTS; 121962306a36Sopenharmony_ci int ret; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 122262306a36Sopenharmony_ci if (!opts) 122362306a36Sopenharmony_ci return NULL; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci ret = fcloop_parse_options(opts, buf); 122662306a36Sopenharmony_ci if (ret) 122762306a36Sopenharmony_ci goto out_free_opts; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* everything there ? */ 123062306a36Sopenharmony_ci if ((opts->mask & opts_mask) != opts_mask) { 123162306a36Sopenharmony_ci ret = -EINVAL; 123262306a36Sopenharmony_ci goto out_free_opts; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci newnport = kzalloc(sizeof(*newnport), GFP_KERNEL); 123662306a36Sopenharmony_ci if (!newnport) 123762306a36Sopenharmony_ci goto out_free_opts; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci INIT_LIST_HEAD(&newnport->nport_list); 124062306a36Sopenharmony_ci newnport->node_name = opts->wwnn; 124162306a36Sopenharmony_ci newnport->port_name = opts->wwpn; 124262306a36Sopenharmony_ci if (opts->mask & NVMF_OPT_ROLES) 124362306a36Sopenharmony_ci newnport->port_role = opts->roles; 124462306a36Sopenharmony_ci if (opts->mask & NVMF_OPT_FCADDR) 124562306a36Sopenharmony_ci newnport->port_id = opts->fcaddr; 124662306a36Sopenharmony_ci kref_init(&newnport->ref); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci list_for_each_entry(tmplport, &fcloop_lports, lport_list) { 125162306a36Sopenharmony_ci if (tmplport->localport->node_name == opts->wwnn && 125262306a36Sopenharmony_ci tmplport->localport->port_name == opts->wwpn) 125362306a36Sopenharmony_ci goto out_invalid_opts; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (tmplport->localport->node_name == opts->lpwwnn && 125662306a36Sopenharmony_ci tmplport->localport->port_name == opts->lpwwpn) 125762306a36Sopenharmony_ci lport = tmplport; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci if (remoteport) { 126162306a36Sopenharmony_ci if (!lport) 126262306a36Sopenharmony_ci goto out_invalid_opts; 126362306a36Sopenharmony_ci newnport->lport = lport; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci list_for_each_entry(nport, &fcloop_nports, nport_list) { 126762306a36Sopenharmony_ci if (nport->node_name == opts->wwnn && 126862306a36Sopenharmony_ci nport->port_name == opts->wwpn) { 126962306a36Sopenharmony_ci if ((remoteport && nport->rport) || 127062306a36Sopenharmony_ci (!remoteport && nport->tport)) { 127162306a36Sopenharmony_ci nport = NULL; 127262306a36Sopenharmony_ci goto out_invalid_opts; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci fcloop_nport_get(nport); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (remoteport) 128062306a36Sopenharmony_ci nport->lport = lport; 128162306a36Sopenharmony_ci if (opts->mask & NVMF_OPT_ROLES) 128262306a36Sopenharmony_ci nport->port_role = opts->roles; 128362306a36Sopenharmony_ci if (opts->mask & NVMF_OPT_FCADDR) 128462306a36Sopenharmony_ci nport->port_id = opts->fcaddr; 128562306a36Sopenharmony_ci goto out_free_newnport; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci list_add_tail(&newnport->nport_list, &fcloop_nports); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci kfree(opts); 129462306a36Sopenharmony_ci return newnport; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ciout_invalid_opts: 129762306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 129862306a36Sopenharmony_ciout_free_newnport: 129962306a36Sopenharmony_ci kfree(newnport); 130062306a36Sopenharmony_ciout_free_opts: 130162306a36Sopenharmony_ci kfree(opts); 130262306a36Sopenharmony_ci return nport; 130362306a36Sopenharmony_ci} 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cistatic ssize_t 130662306a36Sopenharmony_cifcloop_create_remote_port(struct device *dev, struct device_attribute *attr, 130762306a36Sopenharmony_ci const char *buf, size_t count) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci struct nvme_fc_remote_port *remoteport; 131062306a36Sopenharmony_ci struct fcloop_nport *nport; 131162306a36Sopenharmony_ci struct fcloop_rport *rport; 131262306a36Sopenharmony_ci struct nvme_fc_port_info pinfo; 131362306a36Sopenharmony_ci int ret; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci nport = fcloop_alloc_nport(buf, count, true); 131662306a36Sopenharmony_ci if (!nport) 131762306a36Sopenharmony_ci return -EIO; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci memset(&pinfo, 0, sizeof(pinfo)); 132062306a36Sopenharmony_ci pinfo.node_name = nport->node_name; 132162306a36Sopenharmony_ci pinfo.port_name = nport->port_name; 132262306a36Sopenharmony_ci pinfo.port_role = nport->port_role; 132362306a36Sopenharmony_ci pinfo.port_id = nport->port_id; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci ret = nvme_fc_register_remoteport(nport->lport->localport, 132662306a36Sopenharmony_ci &pinfo, &remoteport); 132762306a36Sopenharmony_ci if (ret || !remoteport) { 132862306a36Sopenharmony_ci fcloop_nport_put(nport); 132962306a36Sopenharmony_ci return ret; 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci /* success */ 133362306a36Sopenharmony_ci rport = remoteport->private; 133462306a36Sopenharmony_ci rport->remoteport = remoteport; 133562306a36Sopenharmony_ci rport->targetport = (nport->tport) ? nport->tport->targetport : NULL; 133662306a36Sopenharmony_ci if (nport->tport) { 133762306a36Sopenharmony_ci nport->tport->remoteport = remoteport; 133862306a36Sopenharmony_ci nport->tport->lport = nport->lport; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci rport->nport = nport; 134162306a36Sopenharmony_ci rport->lport = nport->lport; 134262306a36Sopenharmony_ci nport->rport = rport; 134362306a36Sopenharmony_ci spin_lock_init(&rport->lock); 134462306a36Sopenharmony_ci INIT_WORK(&rport->ls_work, fcloop_rport_lsrqst_work); 134562306a36Sopenharmony_ci INIT_LIST_HEAD(&rport->ls_list); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci return count; 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic struct fcloop_rport * 135262306a36Sopenharmony_ci__unlink_remote_port(struct fcloop_nport *nport) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci struct fcloop_rport *rport = nport->rport; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (rport && nport->tport) 135762306a36Sopenharmony_ci nport->tport->remoteport = NULL; 135862306a36Sopenharmony_ci nport->rport = NULL; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci return rport; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic int 136462306a36Sopenharmony_ci__remoteport_unreg(struct fcloop_nport *nport, struct fcloop_rport *rport) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci if (!rport) 136762306a36Sopenharmony_ci return -EALREADY; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci return nvme_fc_unregister_remoteport(rport->remoteport); 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_cistatic ssize_t 137362306a36Sopenharmony_cifcloop_delete_remote_port(struct device *dev, struct device_attribute *attr, 137462306a36Sopenharmony_ci const char *buf, size_t count) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct fcloop_nport *nport = NULL, *tmpport; 137762306a36Sopenharmony_ci static struct fcloop_rport *rport; 137862306a36Sopenharmony_ci u64 nodename, portname; 137962306a36Sopenharmony_ci unsigned long flags; 138062306a36Sopenharmony_ci int ret; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci ret = fcloop_parse_nm_options(dev, &nodename, &portname, buf); 138362306a36Sopenharmony_ci if (ret) 138462306a36Sopenharmony_ci return ret; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci list_for_each_entry(tmpport, &fcloop_nports, nport_list) { 138962306a36Sopenharmony_ci if (tmpport->node_name == nodename && 139062306a36Sopenharmony_ci tmpport->port_name == portname && tmpport->rport) { 139162306a36Sopenharmony_ci nport = tmpport; 139262306a36Sopenharmony_ci rport = __unlink_remote_port(nport); 139362306a36Sopenharmony_ci break; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci if (!nport) 140062306a36Sopenharmony_ci return -ENOENT; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci ret = __remoteport_unreg(nport, rport); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci return ret ? ret : count; 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic ssize_t 140862306a36Sopenharmony_cifcloop_create_target_port(struct device *dev, struct device_attribute *attr, 140962306a36Sopenharmony_ci const char *buf, size_t count) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci struct nvmet_fc_target_port *targetport; 141262306a36Sopenharmony_ci struct fcloop_nport *nport; 141362306a36Sopenharmony_ci struct fcloop_tport *tport; 141462306a36Sopenharmony_ci struct nvmet_fc_port_info tinfo; 141562306a36Sopenharmony_ci int ret; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci nport = fcloop_alloc_nport(buf, count, false); 141862306a36Sopenharmony_ci if (!nport) 141962306a36Sopenharmony_ci return -EIO; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci tinfo.node_name = nport->node_name; 142262306a36Sopenharmony_ci tinfo.port_name = nport->port_name; 142362306a36Sopenharmony_ci tinfo.port_id = nport->port_id; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci ret = nvmet_fc_register_targetport(&tinfo, &tgttemplate, NULL, 142662306a36Sopenharmony_ci &targetport); 142762306a36Sopenharmony_ci if (ret) { 142862306a36Sopenharmony_ci fcloop_nport_put(nport); 142962306a36Sopenharmony_ci return ret; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci /* success */ 143362306a36Sopenharmony_ci tport = targetport->private; 143462306a36Sopenharmony_ci tport->targetport = targetport; 143562306a36Sopenharmony_ci tport->remoteport = (nport->rport) ? nport->rport->remoteport : NULL; 143662306a36Sopenharmony_ci if (nport->rport) 143762306a36Sopenharmony_ci nport->rport->targetport = targetport; 143862306a36Sopenharmony_ci tport->nport = nport; 143962306a36Sopenharmony_ci tport->lport = nport->lport; 144062306a36Sopenharmony_ci nport->tport = tport; 144162306a36Sopenharmony_ci spin_lock_init(&tport->lock); 144262306a36Sopenharmony_ci INIT_WORK(&tport->ls_work, fcloop_tport_lsrqst_work); 144362306a36Sopenharmony_ci INIT_LIST_HEAD(&tport->ls_list); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci return count; 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cistatic struct fcloop_tport * 145062306a36Sopenharmony_ci__unlink_target_port(struct fcloop_nport *nport) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci struct fcloop_tport *tport = nport->tport; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci if (tport && nport->rport) 145562306a36Sopenharmony_ci nport->rport->targetport = NULL; 145662306a36Sopenharmony_ci nport->tport = NULL; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci return tport; 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_cistatic int 146262306a36Sopenharmony_ci__targetport_unreg(struct fcloop_nport *nport, struct fcloop_tport *tport) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci if (!tport) 146562306a36Sopenharmony_ci return -EALREADY; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci return nvmet_fc_unregister_targetport(tport->targetport); 146862306a36Sopenharmony_ci} 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_cistatic ssize_t 147162306a36Sopenharmony_cifcloop_delete_target_port(struct device *dev, struct device_attribute *attr, 147262306a36Sopenharmony_ci const char *buf, size_t count) 147362306a36Sopenharmony_ci{ 147462306a36Sopenharmony_ci struct fcloop_nport *nport = NULL, *tmpport; 147562306a36Sopenharmony_ci struct fcloop_tport *tport = NULL; 147662306a36Sopenharmony_ci u64 nodename, portname; 147762306a36Sopenharmony_ci unsigned long flags; 147862306a36Sopenharmony_ci int ret; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci ret = fcloop_parse_nm_options(dev, &nodename, &portname, buf); 148162306a36Sopenharmony_ci if (ret) 148262306a36Sopenharmony_ci return ret; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci list_for_each_entry(tmpport, &fcloop_nports, nport_list) { 148762306a36Sopenharmony_ci if (tmpport->node_name == nodename && 148862306a36Sopenharmony_ci tmpport->port_name == portname && tmpport->tport) { 148962306a36Sopenharmony_ci nport = tmpport; 149062306a36Sopenharmony_ci tport = __unlink_target_port(nport); 149162306a36Sopenharmony_ci break; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci if (!nport) 149862306a36Sopenharmony_ci return -ENOENT; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci ret = __targetport_unreg(nport, tport); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci return ret ? ret : count; 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_cistatic ssize_t 150662306a36Sopenharmony_cifcloop_set_cmd_drop(struct device *dev, struct device_attribute *attr, 150762306a36Sopenharmony_ci const char *buf, size_t count) 150862306a36Sopenharmony_ci{ 150962306a36Sopenharmony_ci unsigned int opcode; 151062306a36Sopenharmony_ci int starting, amount; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci if (sscanf(buf, "%x:%d:%d", &opcode, &starting, &amount) != 3) 151362306a36Sopenharmony_ci return -EBADRQC; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci drop_current_cnt = 0; 151662306a36Sopenharmony_ci drop_fabric_opcode = (opcode & ~DROP_OPCODE_MASK) ? true : false; 151762306a36Sopenharmony_ci drop_opcode = (opcode & DROP_OPCODE_MASK); 151862306a36Sopenharmony_ci drop_instance = starting; 151962306a36Sopenharmony_ci /* the check to drop routine uses instance + count to know when 152062306a36Sopenharmony_ci * to end. Thus, if dropping 1 instance, count should be 0. 152162306a36Sopenharmony_ci * so subtract 1 from the count. 152262306a36Sopenharmony_ci */ 152362306a36Sopenharmony_ci drop_amount = amount - 1; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci pr_info("%s: DROP: Starting at instance %d of%s opcode x%x drop +%d " 152662306a36Sopenharmony_ci "instances\n", 152762306a36Sopenharmony_ci __func__, drop_instance, drop_fabric_opcode ? " fabric" : "", 152862306a36Sopenharmony_ci drop_opcode, drop_amount); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci return count; 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_cistatic DEVICE_ATTR(add_local_port, 0200, NULL, fcloop_create_local_port); 153562306a36Sopenharmony_cistatic DEVICE_ATTR(del_local_port, 0200, NULL, fcloop_delete_local_port); 153662306a36Sopenharmony_cistatic DEVICE_ATTR(add_remote_port, 0200, NULL, fcloop_create_remote_port); 153762306a36Sopenharmony_cistatic DEVICE_ATTR(del_remote_port, 0200, NULL, fcloop_delete_remote_port); 153862306a36Sopenharmony_cistatic DEVICE_ATTR(add_target_port, 0200, NULL, fcloop_create_target_port); 153962306a36Sopenharmony_cistatic DEVICE_ATTR(del_target_port, 0200, NULL, fcloop_delete_target_port); 154062306a36Sopenharmony_cistatic DEVICE_ATTR(set_cmd_drop, 0200, NULL, fcloop_set_cmd_drop); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic struct attribute *fcloop_dev_attrs[] = { 154362306a36Sopenharmony_ci &dev_attr_add_local_port.attr, 154462306a36Sopenharmony_ci &dev_attr_del_local_port.attr, 154562306a36Sopenharmony_ci &dev_attr_add_remote_port.attr, 154662306a36Sopenharmony_ci &dev_attr_del_remote_port.attr, 154762306a36Sopenharmony_ci &dev_attr_add_target_port.attr, 154862306a36Sopenharmony_ci &dev_attr_del_target_port.attr, 154962306a36Sopenharmony_ci &dev_attr_set_cmd_drop.attr, 155062306a36Sopenharmony_ci NULL 155162306a36Sopenharmony_ci}; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic const struct attribute_group fclopp_dev_attrs_group = { 155462306a36Sopenharmony_ci .attrs = fcloop_dev_attrs, 155562306a36Sopenharmony_ci}; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic const struct attribute_group *fcloop_dev_attr_groups[] = { 155862306a36Sopenharmony_ci &fclopp_dev_attrs_group, 155962306a36Sopenharmony_ci NULL, 156062306a36Sopenharmony_ci}; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic struct class *fcloop_class; 156362306a36Sopenharmony_cistatic struct device *fcloop_device; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_cistatic int __init fcloop_init(void) 156762306a36Sopenharmony_ci{ 156862306a36Sopenharmony_ci int ret; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci fcloop_class = class_create("fcloop"); 157162306a36Sopenharmony_ci if (IS_ERR(fcloop_class)) { 157262306a36Sopenharmony_ci pr_err("couldn't register class fcloop\n"); 157362306a36Sopenharmony_ci ret = PTR_ERR(fcloop_class); 157462306a36Sopenharmony_ci return ret; 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci fcloop_device = device_create_with_groups( 157862306a36Sopenharmony_ci fcloop_class, NULL, MKDEV(0, 0), NULL, 157962306a36Sopenharmony_ci fcloop_dev_attr_groups, "ctl"); 158062306a36Sopenharmony_ci if (IS_ERR(fcloop_device)) { 158162306a36Sopenharmony_ci pr_err("couldn't create ctl device!\n"); 158262306a36Sopenharmony_ci ret = PTR_ERR(fcloop_device); 158362306a36Sopenharmony_ci goto out_destroy_class; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci get_device(fcloop_device); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return 0; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ciout_destroy_class: 159162306a36Sopenharmony_ci class_destroy(fcloop_class); 159262306a36Sopenharmony_ci return ret; 159362306a36Sopenharmony_ci} 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_cistatic void __exit fcloop_exit(void) 159662306a36Sopenharmony_ci{ 159762306a36Sopenharmony_ci struct fcloop_lport *lport = NULL; 159862306a36Sopenharmony_ci struct fcloop_nport *nport = NULL; 159962306a36Sopenharmony_ci struct fcloop_tport *tport; 160062306a36Sopenharmony_ci struct fcloop_rport *rport; 160162306a36Sopenharmony_ci unsigned long flags; 160262306a36Sopenharmony_ci int ret; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci for (;;) { 160762306a36Sopenharmony_ci nport = list_first_entry_or_null(&fcloop_nports, 160862306a36Sopenharmony_ci typeof(*nport), nport_list); 160962306a36Sopenharmony_ci if (!nport) 161062306a36Sopenharmony_ci break; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci tport = __unlink_target_port(nport); 161362306a36Sopenharmony_ci rport = __unlink_remote_port(nport); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci ret = __targetport_unreg(nport, tport); 161862306a36Sopenharmony_ci if (ret) 161962306a36Sopenharmony_ci pr_warn("%s: Failed deleting target port\n", __func__); 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci ret = __remoteport_unreg(nport, rport); 162262306a36Sopenharmony_ci if (ret) 162362306a36Sopenharmony_ci pr_warn("%s: Failed deleting remote port\n", __func__); 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci for (;;) { 162962306a36Sopenharmony_ci lport = list_first_entry_or_null(&fcloop_lports, 163062306a36Sopenharmony_ci typeof(*lport), lport_list); 163162306a36Sopenharmony_ci if (!lport) 163262306a36Sopenharmony_ci break; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci __unlink_local_port(lport); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci ret = __wait_localport_unreg(lport); 163962306a36Sopenharmony_ci if (ret) 164062306a36Sopenharmony_ci pr_warn("%s: Failed deleting local port\n", __func__); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci spin_lock_irqsave(&fcloop_lock, flags); 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci spin_unlock_irqrestore(&fcloop_lock, flags); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci put_device(fcloop_device); 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci device_destroy(fcloop_class, MKDEV(0, 0)); 165062306a36Sopenharmony_ci class_destroy(fcloop_class); 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_cimodule_init(fcloop_init); 165462306a36Sopenharmony_cimodule_exit(fcloop_exit); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1657