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