xref: /openbmc/linux/drivers/nvme/target/fcloop.c (revision 95a9ff33)
1a4b74fccSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0
2475d0fe7SJames Smart /*
3475d0fe7SJames Smart  * Copyright (c) 2016 Avago Technologies.  All rights reserved.
4475d0fe7SJames Smart  */
5475d0fe7SJames Smart #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6475d0fe7SJames Smart #include <linux/module.h>
7475d0fe7SJames Smart #include <linux/parser.h>
8475d0fe7SJames Smart #include <uapi/scsi/fc/fc_fs.h>
9475d0fe7SJames Smart 
10475d0fe7SJames Smart #include "../host/nvme.h"
11475d0fe7SJames Smart #include "../target/nvmet.h"
12475d0fe7SJames Smart #include <linux/nvme-fc-driver.h>
13475d0fe7SJames Smart #include <linux/nvme-fc.h>
14475d0fe7SJames Smart 
15475d0fe7SJames Smart 
16475d0fe7SJames Smart enum {
17475d0fe7SJames Smart 	NVMF_OPT_ERR		= 0,
18475d0fe7SJames Smart 	NVMF_OPT_WWNN		= 1 << 0,
19475d0fe7SJames Smart 	NVMF_OPT_WWPN		= 1 << 1,
20475d0fe7SJames Smart 	NVMF_OPT_ROLES		= 1 << 2,
21475d0fe7SJames Smart 	NVMF_OPT_FCADDR		= 1 << 3,
22475d0fe7SJames Smart 	NVMF_OPT_LPWWNN		= 1 << 4,
23475d0fe7SJames Smart 	NVMF_OPT_LPWWPN		= 1 << 5,
24475d0fe7SJames Smart };
25475d0fe7SJames Smart 
26475d0fe7SJames Smart struct fcloop_ctrl_options {
27475d0fe7SJames Smart 	int			mask;
28475d0fe7SJames Smart 	u64			wwnn;
29475d0fe7SJames Smart 	u64			wwpn;
30475d0fe7SJames Smart 	u32			roles;
31475d0fe7SJames Smart 	u32			fcaddr;
32475d0fe7SJames Smart 	u64			lpwwnn;
33475d0fe7SJames Smart 	u64			lpwwpn;
34475d0fe7SJames Smart };
35475d0fe7SJames Smart 
36475d0fe7SJames Smart static const match_table_t opt_tokens = {
37475d0fe7SJames Smart 	{ NVMF_OPT_WWNN,	"wwnn=%s"	},
38475d0fe7SJames Smart 	{ NVMF_OPT_WWPN,	"wwpn=%s"	},
39475d0fe7SJames Smart 	{ NVMF_OPT_ROLES,	"roles=%d"	},
40475d0fe7SJames Smart 	{ NVMF_OPT_FCADDR,	"fcaddr=%x"	},
41475d0fe7SJames Smart 	{ NVMF_OPT_LPWWNN,	"lpwwnn=%s"	},
42475d0fe7SJames Smart 	{ NVMF_OPT_LPWWPN,	"lpwwpn=%s"	},
43475d0fe7SJames Smart 	{ NVMF_OPT_ERR,		NULL		}
44475d0fe7SJames Smart };
45475d0fe7SJames Smart 
fcloop_verify_addr(substring_t * s)46ca8f4beeSDongli Zhang static int fcloop_verify_addr(substring_t *s)
47ca8f4beeSDongli Zhang {
48ca8f4beeSDongli Zhang 	size_t blen = s->to - s->from + 1;
49ca8f4beeSDongli Zhang 
50ca8f4beeSDongli Zhang 	if (strnlen(s->from, blen) != NVME_FC_TRADDR_HEXNAMELEN + 2 ||
51ca8f4beeSDongli Zhang 	    strncmp(s->from, "0x", 2))
52ca8f4beeSDongli Zhang 		return -EINVAL;
53ca8f4beeSDongli Zhang 
54ca8f4beeSDongli Zhang 	return 0;
55ca8f4beeSDongli Zhang }
56ca8f4beeSDongli Zhang 
57475d0fe7SJames Smart static int
fcloop_parse_options(struct fcloop_ctrl_options * opts,const char * buf)58475d0fe7SJames Smart fcloop_parse_options(struct fcloop_ctrl_options *opts,
59475d0fe7SJames Smart 		const char *buf)
60475d0fe7SJames Smart {
61475d0fe7SJames Smart 	substring_t args[MAX_OPT_ARGS];
62475d0fe7SJames Smart 	char *options, *o, *p;
63475d0fe7SJames Smart 	int token, ret = 0;
64475d0fe7SJames Smart 	u64 token64;
65475d0fe7SJames Smart 
66475d0fe7SJames Smart 	options = o = kstrdup(buf, GFP_KERNEL);
67475d0fe7SJames Smart 	if (!options)
68475d0fe7SJames Smart 		return -ENOMEM;
69475d0fe7SJames Smart 
70475d0fe7SJames Smart 	while ((p = strsep(&o, ",\n")) != NULL) {
71475d0fe7SJames Smart 		if (!*p)
72475d0fe7SJames Smart 			continue;
73475d0fe7SJames Smart 
74475d0fe7SJames Smart 		token = match_token(p, opt_tokens, args);
75475d0fe7SJames Smart 		opts->mask |= token;
76475d0fe7SJames Smart 		switch (token) {
77475d0fe7SJames Smart 		case NVMF_OPT_WWNN:
78ca8f4beeSDongli Zhang 			if (fcloop_verify_addr(args) ||
79ca8f4beeSDongli Zhang 			    match_u64(args, &token64)) {
80475d0fe7SJames Smart 				ret = -EINVAL;
81475d0fe7SJames Smart 				goto out_free_options;
82475d0fe7SJames Smart 			}
83475d0fe7SJames Smart 			opts->wwnn = token64;
84475d0fe7SJames Smart 			break;
85475d0fe7SJames Smart 		case NVMF_OPT_WWPN:
86ca8f4beeSDongli Zhang 			if (fcloop_verify_addr(args) ||
87ca8f4beeSDongli Zhang 			    match_u64(args, &token64)) {
88475d0fe7SJames Smart 				ret = -EINVAL;
89475d0fe7SJames Smart 				goto out_free_options;
90475d0fe7SJames Smart 			}
91475d0fe7SJames Smart 			opts->wwpn = token64;
92475d0fe7SJames Smart 			break;
93475d0fe7SJames Smart 		case NVMF_OPT_ROLES:
94475d0fe7SJames Smart 			if (match_int(args, &token)) {
95475d0fe7SJames Smart 				ret = -EINVAL;
96475d0fe7SJames Smart 				goto out_free_options;
97475d0fe7SJames Smart 			}
98475d0fe7SJames Smart 			opts->roles = token;
99475d0fe7SJames Smart 			break;
100475d0fe7SJames Smart 		case NVMF_OPT_FCADDR:
101475d0fe7SJames Smart 			if (match_hex(args, &token)) {
102475d0fe7SJames Smart 				ret = -EINVAL;
103475d0fe7SJames Smart 				goto out_free_options;
104475d0fe7SJames Smart 			}
105475d0fe7SJames Smart 			opts->fcaddr = token;
106475d0fe7SJames Smart 			break;
107475d0fe7SJames Smart 		case NVMF_OPT_LPWWNN:
108ca8f4beeSDongli Zhang 			if (fcloop_verify_addr(args) ||
109ca8f4beeSDongli Zhang 			    match_u64(args, &token64)) {
110475d0fe7SJames Smart 				ret = -EINVAL;
111475d0fe7SJames Smart 				goto out_free_options;
112475d0fe7SJames Smart 			}
113475d0fe7SJames Smart 			opts->lpwwnn = token64;
114475d0fe7SJames Smart 			break;
115475d0fe7SJames Smart 		case NVMF_OPT_LPWWPN:
116ca8f4beeSDongli Zhang 			if (fcloop_verify_addr(args) ||
117ca8f4beeSDongli Zhang 			    match_u64(args, &token64)) {
118475d0fe7SJames Smart 				ret = -EINVAL;
119475d0fe7SJames Smart 				goto out_free_options;
120475d0fe7SJames Smart 			}
121475d0fe7SJames Smart 			opts->lpwwpn = token64;
122475d0fe7SJames Smart 			break;
123475d0fe7SJames Smart 		default:
124475d0fe7SJames Smart 			pr_warn("unknown parameter or missing value '%s'\n", p);
125475d0fe7SJames Smart 			ret = -EINVAL;
126475d0fe7SJames Smart 			goto out_free_options;
127475d0fe7SJames Smart 		}
128475d0fe7SJames Smart 	}
129475d0fe7SJames Smart 
130475d0fe7SJames Smart out_free_options:
131475d0fe7SJames Smart 	kfree(options);
132475d0fe7SJames Smart 	return ret;
133475d0fe7SJames Smart }
134475d0fe7SJames Smart 
135475d0fe7SJames Smart 
136475d0fe7SJames Smart static int
fcloop_parse_nm_options(struct device * dev,u64 * nname,u64 * pname,const char * buf)137475d0fe7SJames Smart fcloop_parse_nm_options(struct device *dev, u64 *nname, u64 *pname,
138475d0fe7SJames Smart 		const char *buf)
139475d0fe7SJames Smart {
140475d0fe7SJames Smart 	substring_t args[MAX_OPT_ARGS];
141475d0fe7SJames Smart 	char *options, *o, *p;
142475d0fe7SJames Smart 	int token, ret = 0;
143475d0fe7SJames Smart 	u64 token64;
144475d0fe7SJames Smart 
145475d0fe7SJames Smart 	*nname = -1;
146475d0fe7SJames Smart 	*pname = -1;
147475d0fe7SJames Smart 
148475d0fe7SJames Smart 	options = o = kstrdup(buf, GFP_KERNEL);
149475d0fe7SJames Smart 	if (!options)
150475d0fe7SJames Smart 		return -ENOMEM;
151475d0fe7SJames Smart 
152475d0fe7SJames Smart 	while ((p = strsep(&o, ",\n")) != NULL) {
153475d0fe7SJames Smart 		if (!*p)
154475d0fe7SJames Smart 			continue;
155475d0fe7SJames Smart 
156475d0fe7SJames Smart 		token = match_token(p, opt_tokens, args);
157475d0fe7SJames Smart 		switch (token) {
158475d0fe7SJames Smart 		case NVMF_OPT_WWNN:
159ca8f4beeSDongli Zhang 			if (fcloop_verify_addr(args) ||
160ca8f4beeSDongli Zhang 			    match_u64(args, &token64)) {
161475d0fe7SJames Smart 				ret = -EINVAL;
162475d0fe7SJames Smart 				goto out_free_options;
163475d0fe7SJames Smart 			}
164475d0fe7SJames Smart 			*nname = token64;
165475d0fe7SJames Smart 			break;
166475d0fe7SJames Smart 		case NVMF_OPT_WWPN:
167ca8f4beeSDongli Zhang 			if (fcloop_verify_addr(args) ||
168ca8f4beeSDongli Zhang 			    match_u64(args, &token64)) {
169475d0fe7SJames Smart 				ret = -EINVAL;
170475d0fe7SJames Smart 				goto out_free_options;
171475d0fe7SJames Smart 			}
172475d0fe7SJames Smart 			*pname = token64;
173475d0fe7SJames Smart 			break;
174475d0fe7SJames Smart 		default:
175475d0fe7SJames Smart 			pr_warn("unknown parameter or missing value '%s'\n", p);
176475d0fe7SJames Smart 			ret = -EINVAL;
177475d0fe7SJames Smart 			goto out_free_options;
178475d0fe7SJames Smart 		}
179475d0fe7SJames Smart 	}
180475d0fe7SJames Smart 
181475d0fe7SJames Smart out_free_options:
182475d0fe7SJames Smart 	kfree(options);
183475d0fe7SJames Smart 
184475d0fe7SJames Smart 	if (!ret) {
185475d0fe7SJames Smart 		if (*nname == -1)
186475d0fe7SJames Smart 			return -EINVAL;
187475d0fe7SJames Smart 		if (*pname == -1)
188475d0fe7SJames Smart 			return -EINVAL;
189475d0fe7SJames Smart 	}
190475d0fe7SJames Smart 
191475d0fe7SJames Smart 	return ret;
192475d0fe7SJames Smart }
193475d0fe7SJames Smart 
194475d0fe7SJames Smart 
195475d0fe7SJames Smart #define LPORT_OPTS	(NVMF_OPT_WWNN | NVMF_OPT_WWPN)
196475d0fe7SJames Smart 
197475d0fe7SJames Smart #define RPORT_OPTS	(NVMF_OPT_WWNN | NVMF_OPT_WWPN |  \
198475d0fe7SJames Smart 			 NVMF_OPT_LPWWNN | NVMF_OPT_LPWWPN)
199475d0fe7SJames Smart 
200475d0fe7SJames Smart #define TGTPORT_OPTS	(NVMF_OPT_WWNN | NVMF_OPT_WWPN)
201475d0fe7SJames Smart 
202475d0fe7SJames Smart 
203475d0fe7SJames Smart static DEFINE_SPINLOCK(fcloop_lock);
204475d0fe7SJames Smart static LIST_HEAD(fcloop_lports);
205475d0fe7SJames Smart static LIST_HEAD(fcloop_nports);
206475d0fe7SJames Smart 
207475d0fe7SJames Smart struct fcloop_lport {
208475d0fe7SJames Smart 	struct nvme_fc_local_port *localport;
209475d0fe7SJames Smart 	struct list_head lport_list;
210475d0fe7SJames Smart 	struct completion unreg_done;
211475d0fe7SJames Smart };
212475d0fe7SJames Smart 
2136fda2028SJames Smart struct fcloop_lport_priv {
2146fda2028SJames Smart 	struct fcloop_lport *lport;
2156fda2028SJames Smart };
2166fda2028SJames Smart 
217475d0fe7SJames Smart struct fcloop_rport {
218475d0fe7SJames Smart 	struct nvme_fc_remote_port	*remoteport;
219475d0fe7SJames Smart 	struct nvmet_fc_target_port	*targetport;
220475d0fe7SJames Smart 	struct fcloop_nport		*nport;
221475d0fe7SJames Smart 	struct fcloop_lport		*lport;
22238803fcfSJames Smart 	spinlock_t			lock;
22338803fcfSJames Smart 	struct list_head		ls_list;
22438803fcfSJames Smart 	struct work_struct		ls_work;
225475d0fe7SJames Smart };
226475d0fe7SJames Smart 
227475d0fe7SJames Smart struct fcloop_tport {
228475d0fe7SJames Smart 	struct nvmet_fc_target_port	*targetport;
229475d0fe7SJames Smart 	struct nvme_fc_remote_port	*remoteport;
230475d0fe7SJames Smart 	struct fcloop_nport		*nport;
231475d0fe7SJames Smart 	struct fcloop_lport		*lport;
232437c0b82SJames Smart 	spinlock_t			lock;
233437c0b82SJames Smart 	struct list_head		ls_list;
234437c0b82SJames Smart 	struct work_struct		ls_work;
235475d0fe7SJames Smart };
236475d0fe7SJames Smart 
237475d0fe7SJames Smart struct fcloop_nport {
238475d0fe7SJames Smart 	struct fcloop_rport *rport;
239475d0fe7SJames Smart 	struct fcloop_tport *tport;
240475d0fe7SJames Smart 	struct fcloop_lport *lport;
241475d0fe7SJames Smart 	struct list_head nport_list;
242475d0fe7SJames Smart 	struct kref ref;
243475d0fe7SJames Smart 	u64 node_name;
244475d0fe7SJames Smart 	u64 port_name;
245475d0fe7SJames Smart 	u32 port_role;
246475d0fe7SJames Smart 	u32 port_id;
247475d0fe7SJames Smart };
248475d0fe7SJames Smart 
249475d0fe7SJames Smart struct fcloop_lsreq {
250475d0fe7SJames Smart 	struct nvmefc_ls_req		*lsreq;
25172e6329fSJames Smart 	struct nvmefc_ls_rsp		ls_rsp;
252ea397658SJames Smart 	int				lsdir;	/* H2T or T2H */
253475d0fe7SJames Smart 	int				status;
25438803fcfSJames Smart 	struct list_head		ls_list; /* fcloop_rport->ls_list */
255475d0fe7SJames Smart };
256475d0fe7SJames Smart 
2574cf7c363SJames Smart struct fcloop_rscn {
2584cf7c363SJames Smart 	struct fcloop_tport		*tport;
2594cf7c363SJames Smart 	struct work_struct		work;
2604cf7c363SJames Smart };
2614cf7c363SJames Smart 
262b6f80773SJames Smart enum {
263b6f80773SJames Smart 	INI_IO_START		= 0,
264b6f80773SJames Smart 	INI_IO_ACTIVE		= 1,
265b6f80773SJames Smart 	INI_IO_ABORTED		= 2,
266b6f80773SJames Smart 	INI_IO_COMPLETED	= 3,
267b6f80773SJames Smart };
268b6f80773SJames Smart 
269475d0fe7SJames Smart struct fcloop_fcpreq {
270475d0fe7SJames Smart 	struct fcloop_tport		*tport;
271475d0fe7SJames Smart 	struct nvmefc_fcp_req		*fcpreq;
272a97ec51bSJames Smart 	spinlock_t			reqlock;
273475d0fe7SJames Smart 	u16				status;
274b6f80773SJames Smart 	u32				inistate;
275a97ec51bSJames Smart 	bool				active;
276a97ec51bSJames Smart 	bool				aborted;
277b6f80773SJames Smart 	struct kref			ref;
27824431d60SJames Smart 	struct work_struct		fcp_rcv_work;
27924431d60SJames Smart 	struct work_struct		abort_rcv_work;
28024431d60SJames Smart 	struct work_struct		tio_done_work;
281475d0fe7SJames Smart 	struct nvmefc_tgt_fcp_req	tgt_fcp_req;
282475d0fe7SJames Smart };
283475d0fe7SJames Smart 
284ce79bfc2SJames Smart struct fcloop_ini_fcpreq {
285ce79bfc2SJames Smart 	struct nvmefc_fcp_req		*fcpreq;
286ce79bfc2SJames Smart 	struct fcloop_fcpreq		*tfcp_req;
287b6f80773SJames Smart 	spinlock_t			inilock;
288ce79bfc2SJames Smart };
289475d0fe7SJames Smart 
290475d0fe7SJames Smart static inline struct fcloop_lsreq *
ls_rsp_to_lsreq(struct nvmefc_ls_rsp * lsrsp)29172e6329fSJames Smart ls_rsp_to_lsreq(struct nvmefc_ls_rsp *lsrsp)
292475d0fe7SJames Smart {
29372e6329fSJames Smart 	return container_of(lsrsp, struct fcloop_lsreq, ls_rsp);
294475d0fe7SJames Smart }
295475d0fe7SJames Smart 
296475d0fe7SJames Smart static inline struct fcloop_fcpreq *
tgt_fcp_req_to_fcpreq(struct nvmefc_tgt_fcp_req * tgt_fcpreq)297475d0fe7SJames Smart tgt_fcp_req_to_fcpreq(struct nvmefc_tgt_fcp_req *tgt_fcpreq)
298475d0fe7SJames Smart {
299475d0fe7SJames Smart 	return container_of(tgt_fcpreq, struct fcloop_fcpreq, tgt_fcp_req);
300475d0fe7SJames Smart }
301475d0fe7SJames Smart 
302475d0fe7SJames Smart 
303475d0fe7SJames Smart static int
fcloop_create_queue(struct nvme_fc_local_port * localport,unsigned int qidx,u16 qsize,void ** handle)304475d0fe7SJames Smart fcloop_create_queue(struct nvme_fc_local_port *localport,
305475d0fe7SJames Smart 			unsigned int qidx, u16 qsize,
306475d0fe7SJames Smart 			void **handle)
307475d0fe7SJames Smart {
308475d0fe7SJames Smart 	*handle = localport;
309475d0fe7SJames Smart 	return 0;
310475d0fe7SJames Smart }
311475d0fe7SJames Smart 
312475d0fe7SJames Smart static void
fcloop_delete_queue(struct nvme_fc_local_port * localport,unsigned int idx,void * handle)313475d0fe7SJames Smart fcloop_delete_queue(struct nvme_fc_local_port *localport,
314475d0fe7SJames Smart 			unsigned int idx, void *handle)
315475d0fe7SJames Smart {
316475d0fe7SJames Smart }
317475d0fe7SJames Smart 
318475d0fe7SJames Smart static void
fcloop_rport_lsrqst_work(struct work_struct * work)31938803fcfSJames Smart fcloop_rport_lsrqst_work(struct work_struct *work)
320475d0fe7SJames Smart {
32138803fcfSJames Smart 	struct fcloop_rport *rport =
32238803fcfSJames Smart 		container_of(work, struct fcloop_rport, ls_work);
32338803fcfSJames Smart 	struct fcloop_lsreq *tls_req;
324475d0fe7SJames Smart 
32538803fcfSJames Smart 	spin_lock(&rport->lock);
32638803fcfSJames Smart 	for (;;) {
32738803fcfSJames Smart 		tls_req = list_first_entry_or_null(&rport->ls_list,
32838803fcfSJames Smart 				struct fcloop_lsreq, ls_list);
32938803fcfSJames Smart 		if (!tls_req)
33038803fcfSJames Smart 			break;
33138803fcfSJames Smart 
33238803fcfSJames Smart 		list_del(&tls_req->ls_list);
33338803fcfSJames Smart 		spin_unlock(&rport->lock);
33438803fcfSJames Smart 
33538803fcfSJames Smart 		tls_req->lsreq->done(tls_req->lsreq, tls_req->status);
33638803fcfSJames Smart 		/*
33738803fcfSJames Smart 		 * callee may free memory containing tls_req.
33838803fcfSJames Smart 		 * do not reference lsreq after this.
33938803fcfSJames Smart 		 */
34038803fcfSJames Smart 
34138803fcfSJames Smart 		spin_lock(&rport->lock);
34238803fcfSJames Smart 	}
34338803fcfSJames Smart 	spin_unlock(&rport->lock);
344475d0fe7SJames Smart }
345475d0fe7SJames Smart 
346475d0fe7SJames Smart static int
fcloop_h2t_ls_req(struct nvme_fc_local_port * localport,struct nvme_fc_remote_port * remoteport,struct nvmefc_ls_req * lsreq)347ea397658SJames Smart fcloop_h2t_ls_req(struct nvme_fc_local_port *localport,
348475d0fe7SJames Smart 			struct nvme_fc_remote_port *remoteport,
349475d0fe7SJames Smart 			struct nvmefc_ls_req *lsreq)
350475d0fe7SJames Smart {
351475d0fe7SJames Smart 	struct fcloop_lsreq *tls_req = lsreq->private;
352475d0fe7SJames Smart 	struct fcloop_rport *rport = remoteport->private;
353475d0fe7SJames Smart 	int ret = 0;
354475d0fe7SJames Smart 
355475d0fe7SJames Smart 	tls_req->lsreq = lsreq;
35638803fcfSJames Smart 	INIT_LIST_HEAD(&tls_req->ls_list);
357475d0fe7SJames Smart 
358475d0fe7SJames Smart 	if (!rport->targetport) {
359475d0fe7SJames Smart 		tls_req->status = -ECONNREFUSED;
36038803fcfSJames Smart 		spin_lock(&rport->lock);
361*95a9ff33SDaniel Wagner 		list_add_tail(&tls_req->ls_list, &rport->ls_list);
36238803fcfSJames Smart 		spin_unlock(&rport->lock);
3638832cf92SSagi Grimberg 		queue_work(nvmet_wq, &rport->ls_work);
364475d0fe7SJames Smart 		return ret;
365475d0fe7SJames Smart 	}
366475d0fe7SJames Smart 
367475d0fe7SJames Smart 	tls_req->status = 0;
368437c0b82SJames Smart 	ret = nvmet_fc_rcv_ls_req(rport->targetport, rport,
369437c0b82SJames Smart 				  &tls_req->ls_rsp,
370475d0fe7SJames Smart 				  lsreq->rqstaddr, lsreq->rqstlen);
371475d0fe7SJames Smart 
372475d0fe7SJames Smart 	return ret;
373475d0fe7SJames Smart }
374475d0fe7SJames Smart 
375475d0fe7SJames Smart static int
fcloop_h2t_xmt_ls_rsp(struct nvmet_fc_target_port * targetport,struct nvmefc_ls_rsp * lsrsp)376ea397658SJames Smart fcloop_h2t_xmt_ls_rsp(struct nvmet_fc_target_port *targetport,
37772e6329fSJames Smart 			struct nvmefc_ls_rsp *lsrsp)
378475d0fe7SJames Smart {
37972e6329fSJames Smart 	struct fcloop_lsreq *tls_req = ls_rsp_to_lsreq(lsrsp);
380475d0fe7SJames Smart 	struct nvmefc_ls_req *lsreq = tls_req->lsreq;
38138803fcfSJames Smart 	struct fcloop_tport *tport = targetport->private;
38238803fcfSJames Smart 	struct nvme_fc_remote_port *remoteport = tport->remoteport;
38338803fcfSJames Smart 	struct fcloop_rport *rport;
384475d0fe7SJames Smart 
38572e6329fSJames Smart 	memcpy(lsreq->rspaddr, lsrsp->rspbuf,
38672e6329fSJames Smart 		((lsreq->rsplen < lsrsp->rsplen) ?
38772e6329fSJames Smart 				lsreq->rsplen : lsrsp->rsplen));
38838803fcfSJames Smart 
38972e6329fSJames Smart 	lsrsp->done(lsrsp);
390475d0fe7SJames Smart 
39138803fcfSJames Smart 	if (remoteport) {
39238803fcfSJames Smart 		rport = remoteport->private;
39338803fcfSJames Smart 		spin_lock(&rport->lock);
394*95a9ff33SDaniel Wagner 		list_add_tail(&tls_req->ls_list, &rport->ls_list);
39538803fcfSJames Smart 		spin_unlock(&rport->lock);
3968832cf92SSagi Grimberg 		queue_work(nvmet_wq, &rport->ls_work);
39738803fcfSJames Smart 	}
398475d0fe7SJames Smart 
399475d0fe7SJames Smart 	return 0;
400475d0fe7SJames Smart }
401475d0fe7SJames Smart 
402437c0b82SJames Smart static void
fcloop_tport_lsrqst_work(struct work_struct * work)403437c0b82SJames Smart fcloop_tport_lsrqst_work(struct work_struct *work)
404437c0b82SJames Smart {
405437c0b82SJames Smart 	struct fcloop_tport *tport =
406437c0b82SJames Smart 		container_of(work, struct fcloop_tport, ls_work);
407437c0b82SJames Smart 	struct fcloop_lsreq *tls_req;
408437c0b82SJames Smart 
409437c0b82SJames Smart 	spin_lock(&tport->lock);
410437c0b82SJames Smart 	for (;;) {
411437c0b82SJames Smart 		tls_req = list_first_entry_or_null(&tport->ls_list,
412437c0b82SJames Smart 				struct fcloop_lsreq, ls_list);
413437c0b82SJames Smart 		if (!tls_req)
414437c0b82SJames Smart 			break;
415437c0b82SJames Smart 
416437c0b82SJames Smart 		list_del(&tls_req->ls_list);
417437c0b82SJames Smart 		spin_unlock(&tport->lock);
418437c0b82SJames Smart 
419437c0b82SJames Smart 		tls_req->lsreq->done(tls_req->lsreq, tls_req->status);
420437c0b82SJames Smart 		/*
421437c0b82SJames Smart 		 * callee may free memory containing tls_req.
422437c0b82SJames Smart 		 * do not reference lsreq after this.
423437c0b82SJames Smart 		 */
424437c0b82SJames Smart 
425437c0b82SJames Smart 		spin_lock(&tport->lock);
426437c0b82SJames Smart 	}
427437c0b82SJames Smart 	spin_unlock(&tport->lock);
428437c0b82SJames Smart }
429437c0b82SJames Smart 
430437c0b82SJames Smart static int
fcloop_t2h_ls_req(struct nvmet_fc_target_port * targetport,void * hosthandle,struct nvmefc_ls_req * lsreq)431437c0b82SJames Smart fcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle,
432437c0b82SJames Smart 			struct nvmefc_ls_req *lsreq)
433437c0b82SJames Smart {
434437c0b82SJames Smart 	struct fcloop_lsreq *tls_req = lsreq->private;
435437c0b82SJames Smart 	struct fcloop_tport *tport = targetport->private;
436437c0b82SJames Smart 	int ret = 0;
437437c0b82SJames Smart 
438437c0b82SJames Smart 	/*
439437c0b82SJames Smart 	 * hosthandle should be the dst.rport value.
440437c0b82SJames Smart 	 * hosthandle ignored as fcloop currently is
441437c0b82SJames Smart 	 * 1:1 tgtport vs remoteport
442437c0b82SJames Smart 	 */
443437c0b82SJames Smart 	tls_req->lsreq = lsreq;
444437c0b82SJames Smart 	INIT_LIST_HEAD(&tls_req->ls_list);
445437c0b82SJames Smart 
446437c0b82SJames Smart 	if (!tport->remoteport) {
447437c0b82SJames Smart 		tls_req->status = -ECONNREFUSED;
448437c0b82SJames Smart 		spin_lock(&tport->lock);
449*95a9ff33SDaniel Wagner 		list_add_tail(&tls_req->ls_list, &tport->ls_list);
450437c0b82SJames Smart 		spin_unlock(&tport->lock);
4518832cf92SSagi Grimberg 		queue_work(nvmet_wq, &tport->ls_work);
452437c0b82SJames Smart 		return ret;
453437c0b82SJames Smart 	}
454437c0b82SJames Smart 
455437c0b82SJames Smart 	tls_req->status = 0;
456437c0b82SJames Smart 	ret = nvme_fc_rcv_ls_req(tport->remoteport, &tls_req->ls_rsp,
457437c0b82SJames Smart 				 lsreq->rqstaddr, lsreq->rqstlen);
458437c0b82SJames Smart 
459437c0b82SJames Smart 	return ret;
460437c0b82SJames Smart }
461437c0b82SJames Smart 
462437c0b82SJames Smart static int
fcloop_t2h_xmt_ls_rsp(struct nvme_fc_local_port * localport,struct nvme_fc_remote_port * remoteport,struct nvmefc_ls_rsp * lsrsp)463437c0b82SJames Smart fcloop_t2h_xmt_ls_rsp(struct nvme_fc_local_port *localport,
464437c0b82SJames Smart 			struct nvme_fc_remote_port *remoteport,
465437c0b82SJames Smart 			struct nvmefc_ls_rsp *lsrsp)
466437c0b82SJames Smart {
467437c0b82SJames Smart 	struct fcloop_lsreq *tls_req = ls_rsp_to_lsreq(lsrsp);
468437c0b82SJames Smart 	struct nvmefc_ls_req *lsreq = tls_req->lsreq;
469437c0b82SJames Smart 	struct fcloop_rport *rport = remoteport->private;
470437c0b82SJames Smart 	struct nvmet_fc_target_port *targetport = rport->targetport;
471437c0b82SJames Smart 	struct fcloop_tport *tport;
472437c0b82SJames Smart 
473437c0b82SJames Smart 	memcpy(lsreq->rspaddr, lsrsp->rspbuf,
474437c0b82SJames Smart 		((lsreq->rsplen < lsrsp->rsplen) ?
475437c0b82SJames Smart 				lsreq->rsplen : lsrsp->rsplen));
476437c0b82SJames Smart 	lsrsp->done(lsrsp);
477437c0b82SJames Smart 
478437c0b82SJames Smart 	if (targetport) {
479437c0b82SJames Smart 		tport = targetport->private;
480437c0b82SJames Smart 		spin_lock(&tport->lock);
481437c0b82SJames Smart 		list_add_tail(&tport->ls_list, &tls_req->ls_list);
482437c0b82SJames Smart 		spin_unlock(&tport->lock);
4838832cf92SSagi Grimberg 		queue_work(nvmet_wq, &tport->ls_work);
484437c0b82SJames Smart 	}
485437c0b82SJames Smart 
486437c0b82SJames Smart 	return 0;
487437c0b82SJames Smart }
488437c0b82SJames Smart 
489437c0b82SJames Smart static void
fcloop_t2h_host_release(void * hosthandle)490437c0b82SJames Smart fcloop_t2h_host_release(void *hosthandle)
491437c0b82SJames Smart {
492437c0b82SJames Smart 	/* host handle ignored for now */
493437c0b82SJames Smart }
494437c0b82SJames Smart 
4954cf7c363SJames Smart /*
4964cf7c363SJames Smart  * Simulate reception of RSCN and converting it to a initiator transport
4974cf7c363SJames Smart  * call to rescan a remote port.
4984cf7c363SJames Smart  */
4994cf7c363SJames Smart static void
fcloop_tgt_rscn_work(struct work_struct * work)5004cf7c363SJames Smart fcloop_tgt_rscn_work(struct work_struct *work)
5014cf7c363SJames Smart {
5024cf7c363SJames Smart 	struct fcloop_rscn *tgt_rscn =
5034cf7c363SJames Smart 		container_of(work, struct fcloop_rscn, work);
5044cf7c363SJames Smart 	struct fcloop_tport *tport = tgt_rscn->tport;
5054cf7c363SJames Smart 
5064cf7c363SJames Smart 	if (tport->remoteport)
5074cf7c363SJames Smart 		nvme_fc_rescan_remoteport(tport->remoteport);
5084cf7c363SJames Smart 	kfree(tgt_rscn);
5094cf7c363SJames Smart }
5104cf7c363SJames Smart 
5114cf7c363SJames Smart static void
fcloop_tgt_discovery_evt(struct nvmet_fc_target_port * tgtport)5124cf7c363SJames Smart fcloop_tgt_discovery_evt(struct nvmet_fc_target_port *tgtport)
5134cf7c363SJames Smart {
5144cf7c363SJames Smart 	struct fcloop_rscn *tgt_rscn;
5154cf7c363SJames Smart 
5164cf7c363SJames Smart 	tgt_rscn = kzalloc(sizeof(*tgt_rscn), GFP_KERNEL);
5174cf7c363SJames Smart 	if (!tgt_rscn)
5184cf7c363SJames Smart 		return;
5194cf7c363SJames Smart 
5204cf7c363SJames Smart 	tgt_rscn->tport = tgtport->private;
5214cf7c363SJames Smart 	INIT_WORK(&tgt_rscn->work, fcloop_tgt_rscn_work);
5224cf7c363SJames Smart 
5238832cf92SSagi Grimberg 	queue_work(nvmet_wq, &tgt_rscn->work);
5244cf7c363SJames Smart }
5254cf7c363SJames Smart 
526a97ec51bSJames Smart static void
fcloop_tfcp_req_free(struct kref * ref)527b6f80773SJames Smart fcloop_tfcp_req_free(struct kref *ref)
528a97ec51bSJames Smart {
52924431d60SJames Smart 	struct fcloop_fcpreq *tfcp_req =
530b6f80773SJames Smart 		container_of(ref, struct fcloop_fcpreq, ref);
531a97ec51bSJames Smart 
532b6f80773SJames Smart 	kfree(tfcp_req);
53324431d60SJames Smart }
534b6f80773SJames Smart 
535b6f80773SJames Smart static void
fcloop_tfcp_req_put(struct fcloop_fcpreq * tfcp_req)536b6f80773SJames Smart fcloop_tfcp_req_put(struct fcloop_fcpreq *tfcp_req)
537b6f80773SJames Smart {
538b6f80773SJames Smart 	kref_put(&tfcp_req->ref, fcloop_tfcp_req_free);
539b6f80773SJames Smart }
540b6f80773SJames Smart 
541b6f80773SJames Smart static int
fcloop_tfcp_req_get(struct fcloop_fcpreq * tfcp_req)542b6f80773SJames Smart fcloop_tfcp_req_get(struct fcloop_fcpreq *tfcp_req)
543b6f80773SJames Smart {
544b6f80773SJames Smart 	return kref_get_unless_zero(&tfcp_req->ref);
54524431d60SJames Smart }
54624431d60SJames Smart 
54724431d60SJames Smart static void
fcloop_call_host_done(struct nvmefc_fcp_req * fcpreq,struct fcloop_fcpreq * tfcp_req,int status)54824431d60SJames Smart fcloop_call_host_done(struct nvmefc_fcp_req *fcpreq,
54924431d60SJames Smart 			struct fcloop_fcpreq *tfcp_req, int status)
55024431d60SJames Smart {
55124431d60SJames Smart 	struct fcloop_ini_fcpreq *inireq = NULL;
55224431d60SJames Smart 
55324431d60SJames Smart 	if (fcpreq) {
55424431d60SJames Smart 		inireq = fcpreq->private;
555b6f80773SJames Smart 		spin_lock(&inireq->inilock);
55624431d60SJames Smart 		inireq->tfcp_req = NULL;
557b6f80773SJames Smart 		spin_unlock(&inireq->inilock);
55824431d60SJames Smart 
55924431d60SJames Smart 		fcpreq->status = status;
56024431d60SJames Smart 		fcpreq->done(fcpreq);
56124431d60SJames Smart 	}
562b6f80773SJames Smart 
563b6f80773SJames Smart 	/* release original io reference on tgt struct */
564b6f80773SJames Smart 	fcloop_tfcp_req_put(tfcp_req);
565b6f80773SJames Smart }
566b6f80773SJames Smart 
56703d99e5dSJames Smart static bool drop_fabric_opcode;
56803d99e5dSJames Smart #define DROP_OPCODE_MASK	0x00FF
56903d99e5dSJames Smart /* fabrics opcode will have a bit set above 1st byte */
57003d99e5dSJames Smart static int drop_opcode = -1;
57103d99e5dSJames Smart static int drop_instance;
57203d99e5dSJames Smart static int drop_amount;
57303d99e5dSJames Smart static int drop_current_cnt;
57403d99e5dSJames Smart 
57503d99e5dSJames Smart /*
57603d99e5dSJames Smart  * Routine to parse io and determine if the io is to be dropped.
57703d99e5dSJames Smart  * Returns:
57803d99e5dSJames Smart  *  0 if io is not obstructed
57903d99e5dSJames Smart  *  1 if io was dropped
58003d99e5dSJames Smart  */
check_for_drop(struct fcloop_fcpreq * tfcp_req)58103d99e5dSJames Smart static int check_for_drop(struct fcloop_fcpreq *tfcp_req)
58203d99e5dSJames Smart {
58303d99e5dSJames Smart 	struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq;
58403d99e5dSJames Smart 	struct nvme_fc_cmd_iu *cmdiu = fcpreq->cmdaddr;
58503d99e5dSJames Smart 	struct nvme_command *sqe = &cmdiu->sqe;
58603d99e5dSJames Smart 
58703d99e5dSJames Smart 	if (drop_opcode == -1)
58803d99e5dSJames Smart 		return 0;
58903d99e5dSJames Smart 
59003d99e5dSJames Smart 	pr_info("%s: seq opcd x%02x fctype x%02x: drop F %s op x%02x "
59103d99e5dSJames Smart 		"inst %d start %d amt %d\n",
59203d99e5dSJames Smart 		__func__, sqe->common.opcode, sqe->fabrics.fctype,
59303d99e5dSJames Smart 		drop_fabric_opcode ? "y" : "n",
59403d99e5dSJames Smart 		drop_opcode, drop_current_cnt, drop_instance, drop_amount);
59503d99e5dSJames Smart 
59603d99e5dSJames Smart 	if ((drop_fabric_opcode &&
59703d99e5dSJames Smart 	     (sqe->common.opcode != nvme_fabrics_command ||
59803d99e5dSJames Smart 	      sqe->fabrics.fctype != drop_opcode)) ||
59903d99e5dSJames Smart 	    (!drop_fabric_opcode && sqe->common.opcode != drop_opcode))
60003d99e5dSJames Smart 		return 0;
60103d99e5dSJames Smart 
60203d99e5dSJames Smart 	if (++drop_current_cnt >= drop_instance) {
60303d99e5dSJames Smart 		if (drop_current_cnt >= drop_instance + drop_amount)
60403d99e5dSJames Smart 			drop_opcode = -1;
60503d99e5dSJames Smart 		return 1;
60603d99e5dSJames Smart 	}
60703d99e5dSJames Smart 
60803d99e5dSJames Smart 	return 0;
60903d99e5dSJames Smart }
61003d99e5dSJames Smart 
611b6f80773SJames Smart static void
fcloop_fcp_recv_work(struct work_struct * work)612b6f80773SJames Smart fcloop_fcp_recv_work(struct work_struct *work)
613b6f80773SJames Smart {
614b6f80773SJames Smart 	struct fcloop_fcpreq *tfcp_req =
615b6f80773SJames Smart 		container_of(work, struct fcloop_fcpreq, fcp_rcv_work);
616b6f80773SJames Smart 	struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq;
6174f86a6ffSMing Lei 	unsigned long flags;
618b6f80773SJames Smart 	int ret = 0;
619b6f80773SJames Smart 	bool aborted = false;
620b6f80773SJames Smart 
6214f86a6ffSMing Lei 	spin_lock_irqsave(&tfcp_req->reqlock, flags);
622b6f80773SJames Smart 	switch (tfcp_req->inistate) {
623b6f80773SJames Smart 	case INI_IO_START:
624b6f80773SJames Smart 		tfcp_req->inistate = INI_IO_ACTIVE;
625b6f80773SJames Smart 		break;
626b6f80773SJames Smart 	case INI_IO_ABORTED:
627b6f80773SJames Smart 		aborted = true;
628b6f80773SJames Smart 		break;
629b6f80773SJames Smart 	default:
6304f86a6ffSMing Lei 		spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
631b6f80773SJames Smart 		WARN_ON(1);
632b6f80773SJames Smart 		return;
633b6f80773SJames Smart 	}
6344f86a6ffSMing Lei 	spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
635b6f80773SJames Smart 
636b6f80773SJames Smart 	if (unlikely(aborted))
637b6f80773SJames Smart 		ret = -ECANCELED;
63803d99e5dSJames Smart 	else {
63903d99e5dSJames Smart 		if (likely(!check_for_drop(tfcp_req)))
640b6f80773SJames Smart 			ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport,
641b6f80773SJames Smart 				&tfcp_req->tgt_fcp_req,
642b6f80773SJames Smart 				fcpreq->cmdaddr, fcpreq->cmdlen);
64303d99e5dSJames Smart 		else
64403d99e5dSJames Smart 			pr_info("%s: dropped command ********\n", __func__);
64503d99e5dSJames Smart 	}
646b6f80773SJames Smart 	if (ret)
647b6f80773SJames Smart 		fcloop_call_host_done(fcpreq, tfcp_req, ret);
64824431d60SJames Smart }
64924431d60SJames Smart 
65024431d60SJames Smart static void
fcloop_fcp_abort_recv_work(struct work_struct * work)65124431d60SJames Smart fcloop_fcp_abort_recv_work(struct work_struct *work)
65224431d60SJames Smart {
65324431d60SJames Smart 	struct fcloop_fcpreq *tfcp_req =
65424431d60SJames Smart 		container_of(work, struct fcloop_fcpreq, abort_rcv_work);
655b6f80773SJames Smart 	struct nvmefc_fcp_req *fcpreq;
656b6f80773SJames Smart 	bool completed = false;
6574f86a6ffSMing Lei 	unsigned long flags;
658b6f80773SJames Smart 
6594f86a6ffSMing Lei 	spin_lock_irqsave(&tfcp_req->reqlock, flags);
660b6f80773SJames Smart 	fcpreq = tfcp_req->fcpreq;
661b6f80773SJames Smart 	switch (tfcp_req->inistate) {
662b6f80773SJames Smart 	case INI_IO_ABORTED:
663b6f80773SJames Smart 		break;
664b6f80773SJames Smart 	case INI_IO_COMPLETED:
665b6f80773SJames Smart 		completed = true;
666b6f80773SJames Smart 		break;
667b6f80773SJames Smart 	default:
6684f86a6ffSMing Lei 		spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
669b6f80773SJames Smart 		WARN_ON(1);
670b6f80773SJames Smart 		return;
671b6f80773SJames Smart 	}
6724f86a6ffSMing Lei 	spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
673b6f80773SJames Smart 
674b6f80773SJames Smart 	if (unlikely(completed)) {
675b6f80773SJames Smart 		/* remove reference taken in original abort downcall */
676b6f80773SJames Smart 		fcloop_tfcp_req_put(tfcp_req);
677b6f80773SJames Smart 		return;
678b6f80773SJames Smart 	}
67924431d60SJames Smart 
68024431d60SJames Smart 	if (tfcp_req->tport->targetport)
68124431d60SJames Smart 		nvmet_fc_rcv_fcp_abort(tfcp_req->tport->targetport,
68224431d60SJames Smart 					&tfcp_req->tgt_fcp_req);
68324431d60SJames Smart 
6844f86a6ffSMing Lei 	spin_lock_irqsave(&tfcp_req->reqlock, flags);
68524431d60SJames Smart 	tfcp_req->fcpreq = NULL;
6864f86a6ffSMing Lei 	spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
68724431d60SJames Smart 
68824431d60SJames Smart 	fcloop_call_host_done(fcpreq, tfcp_req, -ECANCELED);
689b6f80773SJames Smart 	/* call_host_done releases reference for abort downcall */
690a97ec51bSJames Smart }
691a97ec51bSJames Smart 
692a97ec51bSJames Smart /*
693a97ec51bSJames Smart  * FCP IO operation done by target completion.
694a97ec51bSJames Smart  * call back up initiator "done" flows.
695475d0fe7SJames Smart  */
696475d0fe7SJames Smart static void
fcloop_tgt_fcprqst_done_work(struct work_struct * work)697475d0fe7SJames Smart fcloop_tgt_fcprqst_done_work(struct work_struct *work)
698475d0fe7SJames Smart {
699475d0fe7SJames Smart 	struct fcloop_fcpreq *tfcp_req =
70024431d60SJames Smart 		container_of(work, struct fcloop_fcpreq, tio_done_work);
701a97ec51bSJames Smart 	struct nvmefc_fcp_req *fcpreq;
7024f86a6ffSMing Lei 	unsigned long flags;
703475d0fe7SJames Smart 
7044f86a6ffSMing Lei 	spin_lock_irqsave(&tfcp_req->reqlock, flags);
705a97ec51bSJames Smart 	fcpreq = tfcp_req->fcpreq;
706b6f80773SJames Smart 	tfcp_req->inistate = INI_IO_COMPLETED;
7074f86a6ffSMing Lei 	spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
708a97ec51bSJames Smart 
70924431d60SJames Smart 	fcloop_call_host_done(fcpreq, tfcp_req, tfcp_req->status);
710475d0fe7SJames Smart }
711475d0fe7SJames Smart 
712475d0fe7SJames Smart 
713475d0fe7SJames Smart static int
fcloop_fcp_req(struct nvme_fc_local_port * localport,struct nvme_fc_remote_port * remoteport,void * hw_queue_handle,struct nvmefc_fcp_req * fcpreq)714475d0fe7SJames Smart fcloop_fcp_req(struct nvme_fc_local_port *localport,
715475d0fe7SJames Smart 			struct nvme_fc_remote_port *remoteport,
716475d0fe7SJames Smart 			void *hw_queue_handle,
717475d0fe7SJames Smart 			struct nvmefc_fcp_req *fcpreq)
718475d0fe7SJames Smart {
719475d0fe7SJames Smart 	struct fcloop_rport *rport = remoteport->private;
720ce79bfc2SJames Smart 	struct fcloop_ini_fcpreq *inireq = fcpreq->private;
721ce79bfc2SJames Smart 	struct fcloop_fcpreq *tfcp_req;
722475d0fe7SJames Smart 
723ce79bfc2SJames Smart 	if (!rport->targetport)
724ce79bfc2SJames Smart 		return -ECONNREFUSED;
725475d0fe7SJames Smart 
726e0620bf8SJames Smart 	tfcp_req = kzalloc(sizeof(*tfcp_req), GFP_ATOMIC);
727ce79bfc2SJames Smart 	if (!tfcp_req)
728ce79bfc2SJames Smart 		return -ENOMEM;
729475d0fe7SJames Smart 
730ce79bfc2SJames Smart 	inireq->fcpreq = fcpreq;
731ce79bfc2SJames Smart 	inireq->tfcp_req = tfcp_req;
732b6f80773SJames Smart 	spin_lock_init(&inireq->inilock);
733b6f80773SJames Smart 
734475d0fe7SJames Smart 	tfcp_req->fcpreq = fcpreq;
735475d0fe7SJames Smart 	tfcp_req->tport = rport->targetport->private;
736b6f80773SJames Smart 	tfcp_req->inistate = INI_IO_START;
737a97ec51bSJames Smart 	spin_lock_init(&tfcp_req->reqlock);
73824431d60SJames Smart 	INIT_WORK(&tfcp_req->fcp_rcv_work, fcloop_fcp_recv_work);
73924431d60SJames Smart 	INIT_WORK(&tfcp_req->abort_rcv_work, fcloop_fcp_abort_recv_work);
74024431d60SJames Smart 	INIT_WORK(&tfcp_req->tio_done_work, fcloop_tgt_fcprqst_done_work);
741b6f80773SJames Smart 	kref_init(&tfcp_req->ref);
742475d0fe7SJames Smart 
7438832cf92SSagi Grimberg 	queue_work(nvmet_wq, &tfcp_req->fcp_rcv_work);
744475d0fe7SJames Smart 
74524431d60SJames Smart 	return 0;
746475d0fe7SJames Smart }
747475d0fe7SJames Smart 
748475d0fe7SJames Smart static void
fcloop_fcp_copy_data(u8 op,struct scatterlist * data_sg,struct scatterlist * io_sg,u32 offset,u32 length)749475d0fe7SJames Smart fcloop_fcp_copy_data(u8 op, struct scatterlist *data_sg,
750475d0fe7SJames Smart 			struct scatterlist *io_sg, u32 offset, u32 length)
751475d0fe7SJames Smart {
752475d0fe7SJames Smart 	void *data_p, *io_p;
753475d0fe7SJames Smart 	u32 data_len, io_len, tlen;
754475d0fe7SJames Smart 
755475d0fe7SJames Smart 	io_p = sg_virt(io_sg);
756475d0fe7SJames Smart 	io_len = io_sg->length;
757475d0fe7SJames Smart 
758475d0fe7SJames Smart 	for ( ; offset; ) {
759475d0fe7SJames Smart 		tlen = min_t(u32, offset, io_len);
760475d0fe7SJames Smart 		offset -= tlen;
761475d0fe7SJames Smart 		io_len -= tlen;
762475d0fe7SJames Smart 		if (!io_len) {
763475d0fe7SJames Smart 			io_sg = sg_next(io_sg);
764475d0fe7SJames Smart 			io_p = sg_virt(io_sg);
765475d0fe7SJames Smart 			io_len = io_sg->length;
766475d0fe7SJames Smart 		} else
767475d0fe7SJames Smart 			io_p += tlen;
768475d0fe7SJames Smart 	}
769475d0fe7SJames Smart 
770475d0fe7SJames Smart 	data_p = sg_virt(data_sg);
771475d0fe7SJames Smart 	data_len = data_sg->length;
772475d0fe7SJames Smart 
773475d0fe7SJames Smart 	for ( ; length; ) {
774475d0fe7SJames Smart 		tlen = min_t(u32, io_len, data_len);
775475d0fe7SJames Smart 		tlen = min_t(u32, tlen, length);
776475d0fe7SJames Smart 
777475d0fe7SJames Smart 		if (op == NVMET_FCOP_WRITEDATA)
778475d0fe7SJames Smart 			memcpy(data_p, io_p, tlen);
779475d0fe7SJames Smart 		else
780475d0fe7SJames Smart 			memcpy(io_p, data_p, tlen);
781475d0fe7SJames Smart 
782475d0fe7SJames Smart 		length -= tlen;
783475d0fe7SJames Smart 
784475d0fe7SJames Smart 		io_len -= tlen;
785475d0fe7SJames Smart 		if ((!io_len) && (length)) {
786475d0fe7SJames Smart 			io_sg = sg_next(io_sg);
787475d0fe7SJames Smart 			io_p = sg_virt(io_sg);
788475d0fe7SJames Smart 			io_len = io_sg->length;
789475d0fe7SJames Smart 		} else
790475d0fe7SJames Smart 			io_p += tlen;
791475d0fe7SJames Smart 
792475d0fe7SJames Smart 		data_len -= tlen;
793475d0fe7SJames Smart 		if ((!data_len) && (length)) {
794475d0fe7SJames Smart 			data_sg = sg_next(data_sg);
795475d0fe7SJames Smart 			data_p = sg_virt(data_sg);
796475d0fe7SJames Smart 			data_len = data_sg->length;
797475d0fe7SJames Smart 		} else
798475d0fe7SJames Smart 			data_p += tlen;
799475d0fe7SJames Smart 	}
800475d0fe7SJames Smart }
801475d0fe7SJames Smart 
802475d0fe7SJames Smart static int
fcloop_fcp_op(struct nvmet_fc_target_port * tgtport,struct nvmefc_tgt_fcp_req * tgt_fcpreq)803475d0fe7SJames Smart fcloop_fcp_op(struct nvmet_fc_target_port *tgtport,
804475d0fe7SJames Smart 			struct nvmefc_tgt_fcp_req *tgt_fcpreq)
805475d0fe7SJames Smart {
806475d0fe7SJames Smart 	struct fcloop_fcpreq *tfcp_req = tgt_fcp_req_to_fcpreq(tgt_fcpreq);
807a97ec51bSJames Smart 	struct nvmefc_fcp_req *fcpreq;
808475d0fe7SJames Smart 	u32 rsplen = 0, xfrlen = 0;
809a97ec51bSJames Smart 	int fcp_err = 0, active, aborted;
810475d0fe7SJames Smart 	u8 op = tgt_fcpreq->op;
8114f86a6ffSMing Lei 	unsigned long flags;
812475d0fe7SJames Smart 
8134f86a6ffSMing Lei 	spin_lock_irqsave(&tfcp_req->reqlock, flags);
814a97ec51bSJames Smart 	fcpreq = tfcp_req->fcpreq;
815a97ec51bSJames Smart 	active = tfcp_req->active;
816a97ec51bSJames Smart 	aborted = tfcp_req->aborted;
817a97ec51bSJames Smart 	tfcp_req->active = true;
8184f86a6ffSMing Lei 	spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
819a97ec51bSJames Smart 
820a97ec51bSJames Smart 	if (unlikely(active))
821a97ec51bSJames Smart 		/* illegal - call while i/o active */
822a97ec51bSJames Smart 		return -EALREADY;
823a97ec51bSJames Smart 
824a97ec51bSJames Smart 	if (unlikely(aborted)) {
825a97ec51bSJames Smart 		/* target transport has aborted i/o prior */
8264f86a6ffSMing Lei 		spin_lock_irqsave(&tfcp_req->reqlock, flags);
827a97ec51bSJames Smart 		tfcp_req->active = false;
8284f86a6ffSMing Lei 		spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
829a97ec51bSJames Smart 		tgt_fcpreq->transferred_length = 0;
830a97ec51bSJames Smart 		tgt_fcpreq->fcp_error = -ECANCELED;
831a97ec51bSJames Smart 		tgt_fcpreq->done(tgt_fcpreq);
832a97ec51bSJames Smart 		return 0;
833a97ec51bSJames Smart 	}
834a97ec51bSJames Smart 
835a97ec51bSJames Smart 	/*
836a97ec51bSJames Smart 	 * if fcpreq is NULL, the I/O has been aborted (from
837a97ec51bSJames Smart 	 * initiator side). For the target side, act as if all is well
838a97ec51bSJames Smart 	 * but don't actually move data.
839a97ec51bSJames Smart 	 */
840a97ec51bSJames Smart 
841475d0fe7SJames Smart 	switch (op) {
842475d0fe7SJames Smart 	case NVMET_FCOP_WRITEDATA:
843475d0fe7SJames Smart 		xfrlen = tgt_fcpreq->transfer_length;
844a97ec51bSJames Smart 		if (fcpreq) {
845a97ec51bSJames Smart 			fcloop_fcp_copy_data(op, tgt_fcpreq->sg,
846a97ec51bSJames Smart 					fcpreq->first_sgl, tgt_fcpreq->offset,
847a97ec51bSJames Smart 					xfrlen);
848475d0fe7SJames Smart 			fcpreq->transferred_length += xfrlen;
849a97ec51bSJames Smart 		}
850475d0fe7SJames Smart 		break;
851475d0fe7SJames Smart 
852475d0fe7SJames Smart 	case NVMET_FCOP_READDATA:
853475d0fe7SJames Smart 	case NVMET_FCOP_READDATA_RSP:
854475d0fe7SJames Smart 		xfrlen = tgt_fcpreq->transfer_length;
855a97ec51bSJames Smart 		if (fcpreq) {
856a97ec51bSJames Smart 			fcloop_fcp_copy_data(op, tgt_fcpreq->sg,
857a97ec51bSJames Smart 					fcpreq->first_sgl, tgt_fcpreq->offset,
858a97ec51bSJames Smart 					xfrlen);
859475d0fe7SJames Smart 			fcpreq->transferred_length += xfrlen;
860a97ec51bSJames Smart 		}
861475d0fe7SJames Smart 		if (op == NVMET_FCOP_READDATA)
862475d0fe7SJames Smart 			break;
863475d0fe7SJames Smart 
864475d0fe7SJames Smart 		/* Fall-Thru to RSP handling */
865df561f66SGustavo A. R. Silva 		fallthrough;
866475d0fe7SJames Smart 
867475d0fe7SJames Smart 	case NVMET_FCOP_RSP:
868a97ec51bSJames Smart 		if (fcpreq) {
869475d0fe7SJames Smart 			rsplen = ((fcpreq->rsplen < tgt_fcpreq->rsplen) ?
870475d0fe7SJames Smart 					fcpreq->rsplen : tgt_fcpreq->rsplen);
871475d0fe7SJames Smart 			memcpy(fcpreq->rspaddr, tgt_fcpreq->rspaddr, rsplen);
872475d0fe7SJames Smart 			if (rsplen < tgt_fcpreq->rsplen)
873475d0fe7SJames Smart 				fcp_err = -E2BIG;
874475d0fe7SJames Smart 			fcpreq->rcv_rsplen = rsplen;
875475d0fe7SJames Smart 			fcpreq->status = 0;
876a97ec51bSJames Smart 		}
877475d0fe7SJames Smart 		tfcp_req->status = 0;
878475d0fe7SJames Smart 		break;
879475d0fe7SJames Smart 
880475d0fe7SJames Smart 	default:
881475d0fe7SJames Smart 		fcp_err = -EINVAL;
882475d0fe7SJames Smart 		break;
883475d0fe7SJames Smart 	}
884475d0fe7SJames Smart 
8854f86a6ffSMing Lei 	spin_lock_irqsave(&tfcp_req->reqlock, flags);
886a97ec51bSJames Smart 	tfcp_req->active = false;
8874f86a6ffSMing Lei 	spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
888a97ec51bSJames Smart 
889475d0fe7SJames Smart 	tgt_fcpreq->transferred_length = xfrlen;
890475d0fe7SJames Smart 	tgt_fcpreq->fcp_error = fcp_err;
891475d0fe7SJames Smart 	tgt_fcpreq->done(tgt_fcpreq);
892475d0fe7SJames Smart 
893475d0fe7SJames Smart 	return 0;
894475d0fe7SJames Smart }
895475d0fe7SJames Smart 
896475d0fe7SJames Smart static void
fcloop_tgt_fcp_abort(struct nvmet_fc_target_port * tgtport,struct nvmefc_tgt_fcp_req * tgt_fcpreq)897a97ec51bSJames Smart fcloop_tgt_fcp_abort(struct nvmet_fc_target_port *tgtport,
898a97ec51bSJames Smart 			struct nvmefc_tgt_fcp_req *tgt_fcpreq)
899a97ec51bSJames Smart {
900a97ec51bSJames Smart 	struct fcloop_fcpreq *tfcp_req = tgt_fcp_req_to_fcpreq(tgt_fcpreq);
9014f86a6ffSMing Lei 	unsigned long flags;
902a97ec51bSJames Smart 
903a97ec51bSJames Smart 	/*
904a97ec51bSJames Smart 	 * mark aborted only in case there were 2 threads in transport
905a97ec51bSJames Smart 	 * (one doing io, other doing abort) and only kills ops posted
906a97ec51bSJames Smart 	 * after the abort request
907a97ec51bSJames Smart 	 */
9084f86a6ffSMing Lei 	spin_lock_irqsave(&tfcp_req->reqlock, flags);
909a97ec51bSJames Smart 	tfcp_req->aborted = true;
9104f86a6ffSMing Lei 	spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
911a97ec51bSJames Smart 
912fc9608e8SJames Smart 	tfcp_req->status = NVME_SC_INTERNAL;
913a97ec51bSJames Smart 
914a97ec51bSJames Smart 	/*
915a97ec51bSJames Smart 	 * nothing more to do. If io wasn't active, the transport should
916a97ec51bSJames Smart 	 * immediately call the req_release. If it was active, the op
917a97ec51bSJames Smart 	 * will complete, and the lldd should call req_release.
918a97ec51bSJames Smart 	 */
919a97ec51bSJames Smart }
920a97ec51bSJames Smart 
921a97ec51bSJames Smart static void
fcloop_fcp_req_release(struct nvmet_fc_target_port * tgtport,struct nvmefc_tgt_fcp_req * tgt_fcpreq)92219b58d94SJames Smart fcloop_fcp_req_release(struct nvmet_fc_target_port *tgtport,
92319b58d94SJames Smart 			struct nvmefc_tgt_fcp_req *tgt_fcpreq)
92419b58d94SJames Smart {
92519b58d94SJames Smart 	struct fcloop_fcpreq *tfcp_req = tgt_fcp_req_to_fcpreq(tgt_fcpreq);
92619b58d94SJames Smart 
9278832cf92SSagi Grimberg 	queue_work(nvmet_wq, &tfcp_req->tio_done_work);
92819b58d94SJames Smart }
92919b58d94SJames Smart 
93019b58d94SJames Smart static void
fcloop_h2t_ls_abort(struct nvme_fc_local_port * localport,struct nvme_fc_remote_port * remoteport,struct nvmefc_ls_req * lsreq)931ea397658SJames Smart fcloop_h2t_ls_abort(struct nvme_fc_local_port *localport,
932475d0fe7SJames Smart 			struct nvme_fc_remote_port *remoteport,
933475d0fe7SJames Smart 				struct nvmefc_ls_req *lsreq)
934475d0fe7SJames Smart {
935475d0fe7SJames Smart }
936475d0fe7SJames Smart 
937475d0fe7SJames Smart static void
fcloop_t2h_ls_abort(struct nvmet_fc_target_port * targetport,void * hosthandle,struct nvmefc_ls_req * lsreq)938437c0b82SJames Smart fcloop_t2h_ls_abort(struct nvmet_fc_target_port *targetport,
939437c0b82SJames Smart 			void *hosthandle, struct nvmefc_ls_req *lsreq)
940437c0b82SJames Smart {
941437c0b82SJames Smart }
942437c0b82SJames Smart 
943437c0b82SJames Smart static void
fcloop_fcp_abort(struct nvme_fc_local_port * localport,struct nvme_fc_remote_port * remoteport,void * hw_queue_handle,struct nvmefc_fcp_req * fcpreq)944475d0fe7SJames Smart fcloop_fcp_abort(struct nvme_fc_local_port *localport,
945475d0fe7SJames Smart 			struct nvme_fc_remote_port *remoteport,
946475d0fe7SJames Smart 			void *hw_queue_handle,
947475d0fe7SJames Smart 			struct nvmefc_fcp_req *fcpreq)
948475d0fe7SJames Smart {
949a97ec51bSJames Smart 	struct fcloop_ini_fcpreq *inireq = fcpreq->private;
950b6f80773SJames Smart 	struct fcloop_fcpreq *tfcp_req;
951b6f80773SJames Smart 	bool abortio = true;
9524f86a6ffSMing Lei 	unsigned long flags;
953b6f80773SJames Smart 
954b6f80773SJames Smart 	spin_lock(&inireq->inilock);
955b6f80773SJames Smart 	tfcp_req = inireq->tfcp_req;
956b6f80773SJames Smart 	if (tfcp_req)
957b6f80773SJames Smart 		fcloop_tfcp_req_get(tfcp_req);
958b6f80773SJames Smart 	spin_unlock(&inireq->inilock);
959a97ec51bSJames Smart 
960a97ec51bSJames Smart 	if (!tfcp_req)
961a97ec51bSJames Smart 		/* abort has already been called */
962a97ec51bSJames Smart 		return;
963a97ec51bSJames Smart 
964a97ec51bSJames Smart 	/* break initiator/target relationship for io */
9654f86a6ffSMing Lei 	spin_lock_irqsave(&tfcp_req->reqlock, flags);
966b6f80773SJames Smart 	switch (tfcp_req->inistate) {
967b6f80773SJames Smart 	case INI_IO_START:
968b6f80773SJames Smart 	case INI_IO_ACTIVE:
969b6f80773SJames Smart 		tfcp_req->inistate = INI_IO_ABORTED;
970b6f80773SJames Smart 		break;
971b6f80773SJames Smart 	case INI_IO_COMPLETED:
972b6f80773SJames Smart 		abortio = false;
973b6f80773SJames Smart 		break;
974b6f80773SJames Smart 	default:
9754f86a6ffSMing Lei 		spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
976b6f80773SJames Smart 		WARN_ON(1);
977b6f80773SJames Smart 		return;
978b6f80773SJames Smart 	}
9794f86a6ffSMing Lei 	spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
980a97ec51bSJames Smart 
981b6f80773SJames Smart 	if (abortio)
982b6f80773SJames Smart 		/* leave the reference while the work item is scheduled */
9838832cf92SSagi Grimberg 		WARN_ON(!queue_work(nvmet_wq, &tfcp_req->abort_rcv_work));
984b6f80773SJames Smart 	else  {
985b6f80773SJames Smart 		/*
986b6f80773SJames Smart 		 * as the io has already had the done callback made,
987b6f80773SJames Smart 		 * nothing more to do. So release the reference taken above
988b6f80773SJames Smart 		 */
989b6f80773SJames Smart 		fcloop_tfcp_req_put(tfcp_req);
990b6f80773SJames Smart 	}
991475d0fe7SJames Smart }
992475d0fe7SJames Smart 
993475d0fe7SJames Smart static void
fcloop_nport_free(struct kref * ref)994fddc9923SJames Smart fcloop_nport_free(struct kref *ref)
995fddc9923SJames Smart {
996fddc9923SJames Smart 	struct fcloop_nport *nport =
997fddc9923SJames Smart 		container_of(ref, struct fcloop_nport, ref);
998fddc9923SJames Smart 	unsigned long flags;
999fddc9923SJames Smart 
1000fddc9923SJames Smart 	spin_lock_irqsave(&fcloop_lock, flags);
1001fddc9923SJames Smart 	list_del(&nport->nport_list);
1002fddc9923SJames Smart 	spin_unlock_irqrestore(&fcloop_lock, flags);
1003fddc9923SJames Smart 
1004fddc9923SJames Smart 	kfree(nport);
1005fddc9923SJames Smart }
1006fddc9923SJames Smart 
1007fddc9923SJames Smart static void
fcloop_nport_put(struct fcloop_nport * nport)1008fddc9923SJames Smart fcloop_nport_put(struct fcloop_nport *nport)
1009fddc9923SJames Smart {
1010fddc9923SJames Smart 	kref_put(&nport->ref, fcloop_nport_free);
1011fddc9923SJames Smart }
1012fddc9923SJames Smart 
1013fddc9923SJames Smart static int
fcloop_nport_get(struct fcloop_nport * nport)1014fddc9923SJames Smart fcloop_nport_get(struct fcloop_nport *nport)
1015fddc9923SJames Smart {
1016fddc9923SJames Smart 	return kref_get_unless_zero(&nport->ref);
1017fddc9923SJames Smart }
1018fddc9923SJames Smart 
1019fddc9923SJames Smart static void
fcloop_localport_delete(struct nvme_fc_local_port * localport)1020475d0fe7SJames Smart fcloop_localport_delete(struct nvme_fc_local_port *localport)
1021475d0fe7SJames Smart {
10226fda2028SJames Smart 	struct fcloop_lport_priv *lport_priv = localport->private;
10236fda2028SJames Smart 	struct fcloop_lport *lport = lport_priv->lport;
1024475d0fe7SJames Smart 
1025475d0fe7SJames Smart 	/* release any threads waiting for the unreg to complete */
1026475d0fe7SJames Smart 	complete(&lport->unreg_done);
1027475d0fe7SJames Smart }
1028475d0fe7SJames Smart 
1029475d0fe7SJames Smart static void
fcloop_remoteport_delete(struct nvme_fc_remote_port * remoteport)1030475d0fe7SJames Smart fcloop_remoteport_delete(struct nvme_fc_remote_port *remoteport)
1031475d0fe7SJames Smart {
1032475d0fe7SJames Smart 	struct fcloop_rport *rport = remoteport->private;
1033475d0fe7SJames Smart 
103438803fcfSJames Smart 	flush_work(&rport->ls_work);
1035fddc9923SJames Smart 	fcloop_nport_put(rport->nport);
1036475d0fe7SJames Smart }
1037475d0fe7SJames Smart 
1038475d0fe7SJames Smart static void
fcloop_targetport_delete(struct nvmet_fc_target_port * targetport)1039475d0fe7SJames Smart fcloop_targetport_delete(struct nvmet_fc_target_port *targetport)
1040475d0fe7SJames Smart {
1041475d0fe7SJames Smart 	struct fcloop_tport *tport = targetport->private;
1042475d0fe7SJames Smart 
1043437c0b82SJames Smart 	flush_work(&tport->ls_work);
1044fddc9923SJames Smart 	fcloop_nport_put(tport->nport);
1045475d0fe7SJames Smart }
1046475d0fe7SJames Smart 
1047475d0fe7SJames Smart #define	FCLOOP_HW_QUEUES		4
1048475d0fe7SJames Smart #define	FCLOOP_SGL_SEGS			256
1049475d0fe7SJames Smart #define FCLOOP_DMABOUND_4G		0xFFFFFFFF
1050475d0fe7SJames Smart 
105136b8890eSChristoph Hellwig static struct nvme_fc_port_template fctemplate = {
1052475d0fe7SJames Smart 	.localport_delete	= fcloop_localport_delete,
1053475d0fe7SJames Smart 	.remoteport_delete	= fcloop_remoteport_delete,
1054475d0fe7SJames Smart 	.create_queue		= fcloop_create_queue,
1055475d0fe7SJames Smart 	.delete_queue		= fcloop_delete_queue,
1056ea397658SJames Smart 	.ls_req			= fcloop_h2t_ls_req,
1057475d0fe7SJames Smart 	.fcp_io			= fcloop_fcp_req,
1058ea397658SJames Smart 	.ls_abort		= fcloop_h2t_ls_abort,
1059475d0fe7SJames Smart 	.fcp_abort		= fcloop_fcp_abort,
1060437c0b82SJames Smart 	.xmt_ls_rsp		= fcloop_t2h_xmt_ls_rsp,
1061475d0fe7SJames Smart 	.max_hw_queues		= FCLOOP_HW_QUEUES,
1062475d0fe7SJames Smart 	.max_sgl_segments	= FCLOOP_SGL_SEGS,
1063475d0fe7SJames Smart 	.max_dif_sgl_segments	= FCLOOP_SGL_SEGS,
1064475d0fe7SJames Smart 	.dma_boundary		= FCLOOP_DMABOUND_4G,
1065475d0fe7SJames Smart 	/* sizes of additional private data for data structures */
10666fda2028SJames Smart 	.local_priv_sz		= sizeof(struct fcloop_lport_priv),
1067475d0fe7SJames Smart 	.remote_priv_sz		= sizeof(struct fcloop_rport),
1068475d0fe7SJames Smart 	.lsrqst_priv_sz		= sizeof(struct fcloop_lsreq),
1069ce79bfc2SJames Smart 	.fcprqst_priv_sz	= sizeof(struct fcloop_ini_fcpreq),
1070475d0fe7SJames Smart };
1071475d0fe7SJames Smart 
107236b8890eSChristoph Hellwig static struct nvmet_fc_target_template tgttemplate = {
1073475d0fe7SJames Smart 	.targetport_delete	= fcloop_targetport_delete,
1074ea397658SJames Smart 	.xmt_ls_rsp		= fcloop_h2t_xmt_ls_rsp,
1075475d0fe7SJames Smart 	.fcp_op			= fcloop_fcp_op,
1076a97ec51bSJames Smart 	.fcp_abort		= fcloop_tgt_fcp_abort,
107719b58d94SJames Smart 	.fcp_req_release	= fcloop_fcp_req_release,
10784cf7c363SJames Smart 	.discovery_event	= fcloop_tgt_discovery_evt,
1079437c0b82SJames Smart 	.ls_req			= fcloop_t2h_ls_req,
1080437c0b82SJames Smart 	.ls_abort		= fcloop_t2h_ls_abort,
1081437c0b82SJames Smart 	.host_release		= fcloop_t2h_host_release,
1082475d0fe7SJames Smart 	.max_hw_queues		= FCLOOP_HW_QUEUES,
1083475d0fe7SJames Smart 	.max_sgl_segments	= FCLOOP_SGL_SEGS,
1084475d0fe7SJames Smart 	.max_dif_sgl_segments	= FCLOOP_SGL_SEGS,
1085475d0fe7SJames Smart 	.dma_boundary		= FCLOOP_DMABOUND_4G,
1086475d0fe7SJames Smart 	/* optional features */
108724431d60SJames Smart 	.target_features	= 0,
1088475d0fe7SJames Smart 	/* sizes of additional private data for data structures */
1089475d0fe7SJames Smart 	.target_priv_sz		= sizeof(struct fcloop_tport),
1090437c0b82SJames Smart 	.lsrqst_priv_sz		= sizeof(struct fcloop_lsreq),
1091475d0fe7SJames Smart };
1092475d0fe7SJames Smart 
1093475d0fe7SJames Smart static ssize_t
fcloop_create_local_port(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1094475d0fe7SJames Smart fcloop_create_local_port(struct device *dev, struct device_attribute *attr,
1095475d0fe7SJames Smart 		const char *buf, size_t count)
1096475d0fe7SJames Smart {
1097475d0fe7SJames Smart 	struct nvme_fc_port_info pinfo;
1098475d0fe7SJames Smart 	struct fcloop_ctrl_options *opts;
1099475d0fe7SJames Smart 	struct nvme_fc_local_port *localport;
1100475d0fe7SJames Smart 	struct fcloop_lport *lport;
11016fda2028SJames Smart 	struct fcloop_lport_priv *lport_priv;
11026fda2028SJames Smart 	unsigned long flags;
11036fda2028SJames Smart 	int ret = -ENOMEM;
11046fda2028SJames Smart 
11056fda2028SJames Smart 	lport = kzalloc(sizeof(*lport), GFP_KERNEL);
11066fda2028SJames Smart 	if (!lport)
11076fda2028SJames Smart 		return -ENOMEM;
1108475d0fe7SJames Smart 
1109475d0fe7SJames Smart 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
1110475d0fe7SJames Smart 	if (!opts)
11116fda2028SJames Smart 		goto out_free_lport;
1112475d0fe7SJames Smart 
1113475d0fe7SJames Smart 	ret = fcloop_parse_options(opts, buf);
1114475d0fe7SJames Smart 	if (ret)
1115475d0fe7SJames Smart 		goto out_free_opts;
1116475d0fe7SJames Smart 
1117475d0fe7SJames Smart 	/* everything there ? */
1118475d0fe7SJames Smart 	if ((opts->mask & LPORT_OPTS) != LPORT_OPTS) {
1119475d0fe7SJames Smart 		ret = -EINVAL;
1120475d0fe7SJames Smart 		goto out_free_opts;
1121475d0fe7SJames Smart 	}
1122475d0fe7SJames Smart 
1123fddc9923SJames Smart 	memset(&pinfo, 0, sizeof(pinfo));
1124475d0fe7SJames Smart 	pinfo.node_name = opts->wwnn;
1125475d0fe7SJames Smart 	pinfo.port_name = opts->wwpn;
1126475d0fe7SJames Smart 	pinfo.port_role = opts->roles;
1127475d0fe7SJames Smart 	pinfo.port_id = opts->fcaddr;
1128475d0fe7SJames Smart 
1129475d0fe7SJames Smart 	ret = nvme_fc_register_localport(&pinfo, &fctemplate, NULL, &localport);
1130475d0fe7SJames Smart 	if (!ret) {
1131475d0fe7SJames Smart 		/* success */
11326fda2028SJames Smart 		lport_priv = localport->private;
11336fda2028SJames Smart 		lport_priv->lport = lport;
11346fda2028SJames Smart 
1135475d0fe7SJames Smart 		lport->localport = localport;
1136475d0fe7SJames Smart 		INIT_LIST_HEAD(&lport->lport_list);
1137475d0fe7SJames Smart 
1138475d0fe7SJames Smart 		spin_lock_irqsave(&fcloop_lock, flags);
1139475d0fe7SJames Smart 		list_add_tail(&lport->lport_list, &fcloop_lports);
1140475d0fe7SJames Smart 		spin_unlock_irqrestore(&fcloop_lock, flags);
1141475d0fe7SJames Smart 	}
1142475d0fe7SJames Smart 
1143475d0fe7SJames Smart out_free_opts:
1144475d0fe7SJames Smart 	kfree(opts);
11456fda2028SJames Smart out_free_lport:
11466fda2028SJames Smart 	/* free only if we're going to fail */
11476fda2028SJames Smart 	if (ret)
11486fda2028SJames Smart 		kfree(lport);
11496fda2028SJames Smart 
1150475d0fe7SJames Smart 	return ret ? ret : count;
1151475d0fe7SJames Smart }
1152475d0fe7SJames Smart 
1153475d0fe7SJames Smart 
1154475d0fe7SJames Smart static void
__unlink_local_port(struct fcloop_lport * lport)1155475d0fe7SJames Smart __unlink_local_port(struct fcloop_lport *lport)
1156475d0fe7SJames Smart {
1157475d0fe7SJames Smart 	list_del(&lport->lport_list);
1158475d0fe7SJames Smart }
1159475d0fe7SJames Smart 
1160475d0fe7SJames Smart static int
__wait_localport_unreg(struct fcloop_lport * lport)1161475d0fe7SJames Smart __wait_localport_unreg(struct fcloop_lport *lport)
1162475d0fe7SJames Smart {
1163475d0fe7SJames Smart 	int ret;
1164475d0fe7SJames Smart 
1165475d0fe7SJames Smart 	init_completion(&lport->unreg_done);
1166475d0fe7SJames Smart 
1167475d0fe7SJames Smart 	ret = nvme_fc_unregister_localport(lport->localport);
1168475d0fe7SJames Smart 
1169d97b4111SDaniel Wagner 	if (!ret)
1170475d0fe7SJames Smart 		wait_for_completion(&lport->unreg_done);
1171475d0fe7SJames Smart 
11726fda2028SJames Smart 	kfree(lport);
11736fda2028SJames Smart 
1174475d0fe7SJames Smart 	return ret;
1175475d0fe7SJames Smart }
1176475d0fe7SJames Smart 
1177475d0fe7SJames Smart 
1178475d0fe7SJames Smart static ssize_t
fcloop_delete_local_port(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1179475d0fe7SJames Smart fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
1180475d0fe7SJames Smart 		const char *buf, size_t count)
1181475d0fe7SJames Smart {
1182475d0fe7SJames Smart 	struct fcloop_lport *tlport, *lport = NULL;
1183475d0fe7SJames Smart 	u64 nodename, portname;
1184475d0fe7SJames Smart 	unsigned long flags;
1185475d0fe7SJames Smart 	int ret;
1186475d0fe7SJames Smart 
1187475d0fe7SJames Smart 	ret = fcloop_parse_nm_options(dev, &nodename, &portname, buf);
1188475d0fe7SJames Smart 	if (ret)
1189475d0fe7SJames Smart 		return ret;
1190475d0fe7SJames Smart 
1191475d0fe7SJames Smart 	spin_lock_irqsave(&fcloop_lock, flags);
1192475d0fe7SJames Smart 
1193475d0fe7SJames Smart 	list_for_each_entry(tlport, &fcloop_lports, lport_list) {
1194475d0fe7SJames Smart 		if (tlport->localport->node_name == nodename &&
1195475d0fe7SJames Smart 		    tlport->localport->port_name == portname) {
1196475d0fe7SJames Smart 			lport = tlport;
1197475d0fe7SJames Smart 			__unlink_local_port(lport);
1198475d0fe7SJames Smart 			break;
1199475d0fe7SJames Smart 		}
1200475d0fe7SJames Smart 	}
1201475d0fe7SJames Smart 	spin_unlock_irqrestore(&fcloop_lock, flags);
1202475d0fe7SJames Smart 
1203475d0fe7SJames Smart 	if (!lport)
1204475d0fe7SJames Smart 		return -ENOENT;
1205475d0fe7SJames Smart 
1206475d0fe7SJames Smart 	ret = __wait_localport_unreg(lport);
1207475d0fe7SJames Smart 
1208475d0fe7SJames Smart 	return ret ? ret : count;
1209475d0fe7SJames Smart }
1210475d0fe7SJames Smart 
1211475d0fe7SJames Smart static struct fcloop_nport *
fcloop_alloc_nport(const char * buf,size_t count,bool remoteport)1212475d0fe7SJames Smart fcloop_alloc_nport(const char *buf, size_t count, bool remoteport)
1213475d0fe7SJames Smart {
1214475d0fe7SJames Smart 	struct fcloop_nport *newnport, *nport = NULL;
1215475d0fe7SJames Smart 	struct fcloop_lport *tmplport, *lport = NULL;
1216475d0fe7SJames Smart 	struct fcloop_ctrl_options *opts;
1217475d0fe7SJames Smart 	unsigned long flags;
1218475d0fe7SJames Smart 	u32 opts_mask = (remoteport) ? RPORT_OPTS : TGTPORT_OPTS;
1219475d0fe7SJames Smart 	int ret;
1220475d0fe7SJames Smart 
1221475d0fe7SJames Smart 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
1222475d0fe7SJames Smart 	if (!opts)
1223475d0fe7SJames Smart 		return NULL;
1224475d0fe7SJames Smart 
1225475d0fe7SJames Smart 	ret = fcloop_parse_options(opts, buf);
1226475d0fe7SJames Smart 	if (ret)
1227475d0fe7SJames Smart 		goto out_free_opts;
1228475d0fe7SJames Smart 
1229475d0fe7SJames Smart 	/* everything there ? */
1230475d0fe7SJames Smart 	if ((opts->mask & opts_mask) != opts_mask) {
1231475d0fe7SJames Smart 		ret = -EINVAL;
1232475d0fe7SJames Smart 		goto out_free_opts;
1233475d0fe7SJames Smart 	}
1234475d0fe7SJames Smart 
1235475d0fe7SJames Smart 	newnport = kzalloc(sizeof(*newnport), GFP_KERNEL);
1236475d0fe7SJames Smart 	if (!newnport)
1237475d0fe7SJames Smart 		goto out_free_opts;
1238475d0fe7SJames Smart 
1239475d0fe7SJames Smart 	INIT_LIST_HEAD(&newnport->nport_list);
1240475d0fe7SJames Smart 	newnport->node_name = opts->wwnn;
1241475d0fe7SJames Smart 	newnport->port_name = opts->wwpn;
1242475d0fe7SJames Smart 	if (opts->mask & NVMF_OPT_ROLES)
1243475d0fe7SJames Smart 		newnport->port_role = opts->roles;
1244475d0fe7SJames Smart 	if (opts->mask & NVMF_OPT_FCADDR)
1245475d0fe7SJames Smart 		newnport->port_id = opts->fcaddr;
1246475d0fe7SJames Smart 	kref_init(&newnport->ref);
1247475d0fe7SJames Smart 
1248475d0fe7SJames Smart 	spin_lock_irqsave(&fcloop_lock, flags);
1249475d0fe7SJames Smart 
1250475d0fe7SJames Smart 	list_for_each_entry(tmplport, &fcloop_lports, lport_list) {
1251475d0fe7SJames Smart 		if (tmplport->localport->node_name == opts->wwnn &&
1252475d0fe7SJames Smart 		    tmplport->localport->port_name == opts->wwpn)
1253475d0fe7SJames Smart 			goto out_invalid_opts;
1254475d0fe7SJames Smart 
1255475d0fe7SJames Smart 		if (tmplport->localport->node_name == opts->lpwwnn &&
1256475d0fe7SJames Smart 		    tmplport->localport->port_name == opts->lpwwpn)
1257475d0fe7SJames Smart 			lport = tmplport;
1258475d0fe7SJames Smart 	}
1259475d0fe7SJames Smart 
1260475d0fe7SJames Smart 	if (remoteport) {
1261475d0fe7SJames Smart 		if (!lport)
1262475d0fe7SJames Smart 			goto out_invalid_opts;
1263475d0fe7SJames Smart 		newnport->lport = lport;
1264475d0fe7SJames Smart 	}
1265475d0fe7SJames Smart 
1266475d0fe7SJames Smart 	list_for_each_entry(nport, &fcloop_nports, nport_list) {
1267475d0fe7SJames Smart 		if (nport->node_name == opts->wwnn &&
1268475d0fe7SJames Smart 		    nport->port_name == opts->wwpn) {
1269475d0fe7SJames Smart 			if ((remoteport && nport->rport) ||
1270475d0fe7SJames Smart 			    (!remoteport && nport->tport)) {
1271475d0fe7SJames Smart 				nport = NULL;
1272475d0fe7SJames Smart 				goto out_invalid_opts;
1273475d0fe7SJames Smart 			}
1274475d0fe7SJames Smart 
1275475d0fe7SJames Smart 			fcloop_nport_get(nport);
1276475d0fe7SJames Smart 
1277475d0fe7SJames Smart 			spin_unlock_irqrestore(&fcloop_lock, flags);
1278475d0fe7SJames Smart 
1279475d0fe7SJames Smart 			if (remoteport)
1280475d0fe7SJames Smart 				nport->lport = lport;
1281475d0fe7SJames Smart 			if (opts->mask & NVMF_OPT_ROLES)
1282475d0fe7SJames Smart 				nport->port_role = opts->roles;
1283475d0fe7SJames Smart 			if (opts->mask & NVMF_OPT_FCADDR)
1284475d0fe7SJames Smart 				nport->port_id = opts->fcaddr;
1285475d0fe7SJames Smart 			goto out_free_newnport;
1286475d0fe7SJames Smart 		}
1287475d0fe7SJames Smart 	}
1288475d0fe7SJames Smart 
1289475d0fe7SJames Smart 	list_add_tail(&newnport->nport_list, &fcloop_nports);
1290475d0fe7SJames Smart 
1291475d0fe7SJames Smart 	spin_unlock_irqrestore(&fcloop_lock, flags);
1292475d0fe7SJames Smart 
1293475d0fe7SJames Smart 	kfree(opts);
1294475d0fe7SJames Smart 	return newnport;
1295475d0fe7SJames Smart 
1296475d0fe7SJames Smart out_invalid_opts:
1297475d0fe7SJames Smart 	spin_unlock_irqrestore(&fcloop_lock, flags);
1298475d0fe7SJames Smart out_free_newnport:
1299475d0fe7SJames Smart 	kfree(newnport);
1300475d0fe7SJames Smart out_free_opts:
1301475d0fe7SJames Smart 	kfree(opts);
1302475d0fe7SJames Smart 	return nport;
1303475d0fe7SJames Smart }
1304475d0fe7SJames Smart 
1305475d0fe7SJames Smart static ssize_t
fcloop_create_remote_port(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1306475d0fe7SJames Smart fcloop_create_remote_port(struct device *dev, struct device_attribute *attr,
1307475d0fe7SJames Smart 		const char *buf, size_t count)
1308475d0fe7SJames Smart {
1309475d0fe7SJames Smart 	struct nvme_fc_remote_port *remoteport;
1310475d0fe7SJames Smart 	struct fcloop_nport *nport;
1311475d0fe7SJames Smart 	struct fcloop_rport *rport;
1312475d0fe7SJames Smart 	struct nvme_fc_port_info pinfo;
1313475d0fe7SJames Smart 	int ret;
1314475d0fe7SJames Smart 
1315475d0fe7SJames Smart 	nport = fcloop_alloc_nport(buf, count, true);
1316475d0fe7SJames Smart 	if (!nport)
1317475d0fe7SJames Smart 		return -EIO;
1318475d0fe7SJames Smart 
1319fddc9923SJames Smart 	memset(&pinfo, 0, sizeof(pinfo));
1320475d0fe7SJames Smart 	pinfo.node_name = nport->node_name;
1321475d0fe7SJames Smart 	pinfo.port_name = nport->port_name;
1322475d0fe7SJames Smart 	pinfo.port_role = nport->port_role;
1323475d0fe7SJames Smart 	pinfo.port_id = nport->port_id;
1324475d0fe7SJames Smart 
1325475d0fe7SJames Smart 	ret = nvme_fc_register_remoteport(nport->lport->localport,
1326475d0fe7SJames Smart 						&pinfo, &remoteport);
1327475d0fe7SJames Smart 	if (ret || !remoteport) {
1328475d0fe7SJames Smart 		fcloop_nport_put(nport);
1329475d0fe7SJames Smart 		return ret;
1330475d0fe7SJames Smart 	}
1331475d0fe7SJames Smart 
1332475d0fe7SJames Smart 	/* success */
1333475d0fe7SJames Smart 	rport = remoteport->private;
1334475d0fe7SJames Smart 	rport->remoteport = remoteport;
1335475d0fe7SJames Smart 	rport->targetport = (nport->tport) ?  nport->tport->targetport : NULL;
1336475d0fe7SJames Smart 	if (nport->tport) {
1337475d0fe7SJames Smart 		nport->tport->remoteport = remoteport;
1338475d0fe7SJames Smart 		nport->tport->lport = nport->lport;
1339475d0fe7SJames Smart 	}
1340475d0fe7SJames Smart 	rport->nport = nport;
1341475d0fe7SJames Smart 	rport->lport = nport->lport;
1342475d0fe7SJames Smart 	nport->rport = rport;
134338803fcfSJames Smart 	spin_lock_init(&rport->lock);
134438803fcfSJames Smart 	INIT_WORK(&rport->ls_work, fcloop_rport_lsrqst_work);
134538803fcfSJames Smart 	INIT_LIST_HEAD(&rport->ls_list);
1346475d0fe7SJames Smart 
13477c3a23b8SColin Ian King 	return count;
1348475d0fe7SJames Smart }
1349475d0fe7SJames Smart 
1350475d0fe7SJames Smart 
1351475d0fe7SJames Smart static struct fcloop_rport *
__unlink_remote_port(struct fcloop_nport * nport)1352475d0fe7SJames Smart __unlink_remote_port(struct fcloop_nport *nport)
1353475d0fe7SJames Smart {
1354475d0fe7SJames Smart 	struct fcloop_rport *rport = nport->rport;
1355475d0fe7SJames Smart 
1356475d0fe7SJames Smart 	if (rport && nport->tport)
1357475d0fe7SJames Smart 		nport->tport->remoteport = NULL;
1358475d0fe7SJames Smart 	nport->rport = NULL;
1359475d0fe7SJames Smart 
1360475d0fe7SJames Smart 	return rport;
1361475d0fe7SJames Smart }
1362475d0fe7SJames Smart 
1363475d0fe7SJames Smart static int
__remoteport_unreg(struct fcloop_nport * nport,struct fcloop_rport * rport)1364fddc9923SJames Smart __remoteport_unreg(struct fcloop_nport *nport, struct fcloop_rport *rport)
1365475d0fe7SJames Smart {
1366475d0fe7SJames Smart 	if (!rport)
1367475d0fe7SJames Smart 		return -EALREADY;
1368475d0fe7SJames Smart 
1369fddc9923SJames Smart 	return nvme_fc_unregister_remoteport(rport->remoteport);
1370475d0fe7SJames Smart }
1371475d0fe7SJames Smart 
1372475d0fe7SJames Smart static ssize_t
fcloop_delete_remote_port(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1373475d0fe7SJames Smart fcloop_delete_remote_port(struct device *dev, struct device_attribute *attr,
1374475d0fe7SJames Smart 		const char *buf, size_t count)
1375475d0fe7SJames Smart {
1376475d0fe7SJames Smart 	struct fcloop_nport *nport = NULL, *tmpport;
1377475d0fe7SJames Smart 	static struct fcloop_rport *rport;
1378475d0fe7SJames Smart 	u64 nodename, portname;
1379475d0fe7SJames Smart 	unsigned long flags;
1380475d0fe7SJames Smart 	int ret;
1381475d0fe7SJames Smart 
1382475d0fe7SJames Smart 	ret = fcloop_parse_nm_options(dev, &nodename, &portname, buf);
1383475d0fe7SJames Smart 	if (ret)
1384475d0fe7SJames Smart 		return ret;
1385475d0fe7SJames Smart 
1386475d0fe7SJames Smart 	spin_lock_irqsave(&fcloop_lock, flags);
1387475d0fe7SJames Smart 
1388475d0fe7SJames Smart 	list_for_each_entry(tmpport, &fcloop_nports, nport_list) {
1389475d0fe7SJames Smart 		if (tmpport->node_name == nodename &&
1390475d0fe7SJames Smart 		    tmpport->port_name == portname && tmpport->rport) {
1391475d0fe7SJames Smart 			nport = tmpport;
1392475d0fe7SJames Smart 			rport = __unlink_remote_port(nport);
1393475d0fe7SJames Smart 			break;
1394475d0fe7SJames Smart 		}
1395475d0fe7SJames Smart 	}
1396475d0fe7SJames Smart 
1397475d0fe7SJames Smart 	spin_unlock_irqrestore(&fcloop_lock, flags);
1398475d0fe7SJames Smart 
1399475d0fe7SJames Smart 	if (!nport)
1400475d0fe7SJames Smart 		return -ENOENT;
1401475d0fe7SJames Smart 
1402fddc9923SJames Smart 	ret = __remoteport_unreg(nport, rport);
1403475d0fe7SJames Smart 
1404475d0fe7SJames Smart 	return ret ? ret : count;
1405475d0fe7SJames Smart }
1406475d0fe7SJames Smart 
1407475d0fe7SJames Smart static ssize_t
fcloop_create_target_port(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1408475d0fe7SJames Smart fcloop_create_target_port(struct device *dev, struct device_attribute *attr,
1409475d0fe7SJames Smart 		const char *buf, size_t count)
1410475d0fe7SJames Smart {
1411475d0fe7SJames Smart 	struct nvmet_fc_target_port *targetport;
1412475d0fe7SJames Smart 	struct fcloop_nport *nport;
1413475d0fe7SJames Smart 	struct fcloop_tport *tport;
1414475d0fe7SJames Smart 	struct nvmet_fc_port_info tinfo;
1415475d0fe7SJames Smart 	int ret;
1416475d0fe7SJames Smart 
1417475d0fe7SJames Smart 	nport = fcloop_alloc_nport(buf, count, false);
1418475d0fe7SJames Smart 	if (!nport)
1419475d0fe7SJames Smart 		return -EIO;
1420475d0fe7SJames Smart 
1421475d0fe7SJames Smart 	tinfo.node_name = nport->node_name;
1422475d0fe7SJames Smart 	tinfo.port_name = nport->port_name;
1423475d0fe7SJames Smart 	tinfo.port_id = nport->port_id;
1424475d0fe7SJames Smart 
1425475d0fe7SJames Smart 	ret = nvmet_fc_register_targetport(&tinfo, &tgttemplate, NULL,
1426475d0fe7SJames Smart 						&targetport);
1427475d0fe7SJames Smart 	if (ret) {
1428475d0fe7SJames Smart 		fcloop_nport_put(nport);
1429475d0fe7SJames Smart 		return ret;
1430475d0fe7SJames Smart 	}
1431475d0fe7SJames Smart 
1432475d0fe7SJames Smart 	/* success */
1433475d0fe7SJames Smart 	tport = targetport->private;
1434475d0fe7SJames Smart 	tport->targetport = targetport;
1435475d0fe7SJames Smart 	tport->remoteport = (nport->rport) ?  nport->rport->remoteport : NULL;
1436475d0fe7SJames Smart 	if (nport->rport)
1437475d0fe7SJames Smart 		nport->rport->targetport = targetport;
1438475d0fe7SJames Smart 	tport->nport = nport;
1439475d0fe7SJames Smart 	tport->lport = nport->lport;
1440475d0fe7SJames Smart 	nport->tport = tport;
1441437c0b82SJames Smart 	spin_lock_init(&tport->lock);
1442437c0b82SJames Smart 	INIT_WORK(&tport->ls_work, fcloop_tport_lsrqst_work);
1443437c0b82SJames Smart 	INIT_LIST_HEAD(&tport->ls_list);
1444475d0fe7SJames Smart 
14457c3a23b8SColin Ian King 	return count;
1446475d0fe7SJames Smart }
1447475d0fe7SJames Smart 
1448475d0fe7SJames Smart 
1449475d0fe7SJames Smart static struct fcloop_tport *
__unlink_target_port(struct fcloop_nport * nport)1450475d0fe7SJames Smart __unlink_target_port(struct fcloop_nport *nport)
1451475d0fe7SJames Smart {
1452475d0fe7SJames Smart 	struct fcloop_tport *tport = nport->tport;
1453475d0fe7SJames Smart 
1454475d0fe7SJames Smart 	if (tport && nport->rport)
1455475d0fe7SJames Smart 		nport->rport->targetport = NULL;
1456475d0fe7SJames Smart 	nport->tport = NULL;
1457475d0fe7SJames Smart 
1458475d0fe7SJames Smart 	return tport;
1459475d0fe7SJames Smart }
1460475d0fe7SJames Smart 
1461475d0fe7SJames Smart static int
__targetport_unreg(struct fcloop_nport * nport,struct fcloop_tport * tport)1462fddc9923SJames Smart __targetport_unreg(struct fcloop_nport *nport, struct fcloop_tport *tport)
1463475d0fe7SJames Smart {
1464475d0fe7SJames Smart 	if (!tport)
1465475d0fe7SJames Smart 		return -EALREADY;
1466475d0fe7SJames Smart 
1467fddc9923SJames Smart 	return nvmet_fc_unregister_targetport(tport->targetport);
1468475d0fe7SJames Smart }
1469475d0fe7SJames Smart 
1470475d0fe7SJames Smart static ssize_t
fcloop_delete_target_port(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1471475d0fe7SJames Smart fcloop_delete_target_port(struct device *dev, struct device_attribute *attr,
1472475d0fe7SJames Smart 		const char *buf, size_t count)
1473475d0fe7SJames Smart {
1474475d0fe7SJames Smart 	struct fcloop_nport *nport = NULL, *tmpport;
1475254beb84SJames Smart 	struct fcloop_tport *tport = NULL;
1476475d0fe7SJames Smart 	u64 nodename, portname;
1477475d0fe7SJames Smart 	unsigned long flags;
1478475d0fe7SJames Smart 	int ret;
1479475d0fe7SJames Smart 
1480475d0fe7SJames Smart 	ret = fcloop_parse_nm_options(dev, &nodename, &portname, buf);
1481475d0fe7SJames Smart 	if (ret)
1482475d0fe7SJames Smart 		return ret;
1483475d0fe7SJames Smart 
1484475d0fe7SJames Smart 	spin_lock_irqsave(&fcloop_lock, flags);
1485475d0fe7SJames Smart 
1486475d0fe7SJames Smart 	list_for_each_entry(tmpport, &fcloop_nports, nport_list) {
1487475d0fe7SJames Smart 		if (tmpport->node_name == nodename &&
1488475d0fe7SJames Smart 		    tmpport->port_name == portname && tmpport->tport) {
1489475d0fe7SJames Smart 			nport = tmpport;
1490475d0fe7SJames Smart 			tport = __unlink_target_port(nport);
1491475d0fe7SJames Smart 			break;
1492475d0fe7SJames Smart 		}
1493475d0fe7SJames Smart 	}
1494475d0fe7SJames Smart 
1495475d0fe7SJames Smart 	spin_unlock_irqrestore(&fcloop_lock, flags);
1496475d0fe7SJames Smart 
1497475d0fe7SJames Smart 	if (!nport)
1498475d0fe7SJames Smart 		return -ENOENT;
1499475d0fe7SJames Smart 
1500fddc9923SJames Smart 	ret = __targetport_unreg(nport, tport);
1501475d0fe7SJames Smart 
1502475d0fe7SJames Smart 	return ret ? ret : count;
1503475d0fe7SJames Smart }
1504475d0fe7SJames Smart 
150503d99e5dSJames Smart static ssize_t
fcloop_set_cmd_drop(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)150603d99e5dSJames Smart fcloop_set_cmd_drop(struct device *dev, struct device_attribute *attr,
150703d99e5dSJames Smart 		const char *buf, size_t count)
150803d99e5dSJames Smart {
15092b54996bSJames Smart 	unsigned int opcode;
15102b54996bSJames Smart 	int starting, amount;
151103d99e5dSJames Smart 
151203d99e5dSJames Smart 	if (sscanf(buf, "%x:%d:%d", &opcode, &starting, &amount) != 3)
151303d99e5dSJames Smart 		return -EBADRQC;
151403d99e5dSJames Smart 
151503d99e5dSJames Smart 	drop_current_cnt = 0;
151603d99e5dSJames Smart 	drop_fabric_opcode = (opcode & ~DROP_OPCODE_MASK) ? true : false;
151703d99e5dSJames Smart 	drop_opcode = (opcode & DROP_OPCODE_MASK);
151803d99e5dSJames Smart 	drop_instance = starting;
151903d99e5dSJames Smart 	/* the check to drop routine uses instance + count to know when
152003d99e5dSJames Smart 	 * to end. Thus, if dropping 1 instance, count should be 0.
152103d99e5dSJames Smart 	 * so subtract 1 from the count.
152203d99e5dSJames Smart 	 */
152303d99e5dSJames Smart 	drop_amount = amount - 1;
152403d99e5dSJames Smart 
152503d99e5dSJames Smart 	pr_info("%s: DROP: Starting at instance %d of%s opcode x%x drop +%d "
152603d99e5dSJames Smart 		"instances\n",
152703d99e5dSJames Smart 		__func__, drop_instance, drop_fabric_opcode ? " fabric" : "",
152803d99e5dSJames Smart 		drop_opcode, drop_amount);
152903d99e5dSJames Smart 
153003d99e5dSJames Smart 	return count;
153103d99e5dSJames Smart }
153203d99e5dSJames Smart 
1533475d0fe7SJames Smart 
1534475d0fe7SJames Smart static DEVICE_ATTR(add_local_port, 0200, NULL, fcloop_create_local_port);
1535475d0fe7SJames Smart static DEVICE_ATTR(del_local_port, 0200, NULL, fcloop_delete_local_port);
1536475d0fe7SJames Smart static DEVICE_ATTR(add_remote_port, 0200, NULL, fcloop_create_remote_port);
1537475d0fe7SJames Smart static DEVICE_ATTR(del_remote_port, 0200, NULL, fcloop_delete_remote_port);
1538475d0fe7SJames Smart static DEVICE_ATTR(add_target_port, 0200, NULL, fcloop_create_target_port);
1539475d0fe7SJames Smart static DEVICE_ATTR(del_target_port, 0200, NULL, fcloop_delete_target_port);
154003d99e5dSJames Smart static DEVICE_ATTR(set_cmd_drop, 0200, NULL, fcloop_set_cmd_drop);
1541475d0fe7SJames Smart 
1542475d0fe7SJames Smart static struct attribute *fcloop_dev_attrs[] = {
1543475d0fe7SJames Smart 	&dev_attr_add_local_port.attr,
1544475d0fe7SJames Smart 	&dev_attr_del_local_port.attr,
1545475d0fe7SJames Smart 	&dev_attr_add_remote_port.attr,
1546475d0fe7SJames Smart 	&dev_attr_del_remote_port.attr,
1547475d0fe7SJames Smart 	&dev_attr_add_target_port.attr,
1548475d0fe7SJames Smart 	&dev_attr_del_target_port.attr,
154903d99e5dSJames Smart 	&dev_attr_set_cmd_drop.attr,
1550475d0fe7SJames Smart 	NULL
1551475d0fe7SJames Smart };
1552475d0fe7SJames Smart 
155360b152a5SRikard Falkeborn static const struct attribute_group fclopp_dev_attrs_group = {
1554475d0fe7SJames Smart 	.attrs		= fcloop_dev_attrs,
1555475d0fe7SJames Smart };
1556475d0fe7SJames Smart 
1557475d0fe7SJames Smart static const struct attribute_group *fcloop_dev_attr_groups[] = {
1558475d0fe7SJames Smart 	&fclopp_dev_attrs_group,
1559475d0fe7SJames Smart 	NULL,
1560475d0fe7SJames Smart };
1561475d0fe7SJames Smart 
1562475d0fe7SJames Smart static struct class *fcloop_class;
1563475d0fe7SJames Smart static struct device *fcloop_device;
1564475d0fe7SJames Smart 
1565475d0fe7SJames Smart 
fcloop_init(void)1566475d0fe7SJames Smart static int __init fcloop_init(void)
1567475d0fe7SJames Smart {
1568475d0fe7SJames Smart 	int ret;
1569475d0fe7SJames Smart 
15701aaba11dSGreg Kroah-Hartman 	fcloop_class = class_create("fcloop");
1571475d0fe7SJames Smart 	if (IS_ERR(fcloop_class)) {
1572475d0fe7SJames Smart 		pr_err("couldn't register class fcloop\n");
1573475d0fe7SJames Smart 		ret = PTR_ERR(fcloop_class);
1574475d0fe7SJames Smart 		return ret;
1575475d0fe7SJames Smart 	}
1576475d0fe7SJames Smart 
1577475d0fe7SJames Smart 	fcloop_device = device_create_with_groups(
1578475d0fe7SJames Smart 				fcloop_class, NULL, MKDEV(0, 0), NULL,
1579475d0fe7SJames Smart 				fcloop_dev_attr_groups, "ctl");
1580475d0fe7SJames Smart 	if (IS_ERR(fcloop_device)) {
1581475d0fe7SJames Smart 		pr_err("couldn't create ctl device!\n");
1582475d0fe7SJames Smart 		ret = PTR_ERR(fcloop_device);
1583475d0fe7SJames Smart 		goto out_destroy_class;
1584475d0fe7SJames Smart 	}
1585475d0fe7SJames Smart 
1586475d0fe7SJames Smart 	get_device(fcloop_device);
1587475d0fe7SJames Smart 
1588475d0fe7SJames Smart 	return 0;
1589475d0fe7SJames Smart 
1590475d0fe7SJames Smart out_destroy_class:
1591475d0fe7SJames Smart 	class_destroy(fcloop_class);
1592475d0fe7SJames Smart 	return ret;
1593475d0fe7SJames Smart }
1594475d0fe7SJames Smart 
fcloop_exit(void)1595475d0fe7SJames Smart static void __exit fcloop_exit(void)
1596475d0fe7SJames Smart {
15972b54996bSJames Smart 	struct fcloop_lport *lport = NULL;
15982b54996bSJames Smart 	struct fcloop_nport *nport = NULL;
1599475d0fe7SJames Smart 	struct fcloop_tport *tport;
1600475d0fe7SJames Smart 	struct fcloop_rport *rport;
1601475d0fe7SJames Smart 	unsigned long flags;
1602475d0fe7SJames Smart 	int ret;
1603475d0fe7SJames Smart 
1604475d0fe7SJames Smart 	spin_lock_irqsave(&fcloop_lock, flags);
1605475d0fe7SJames Smart 
1606475d0fe7SJames Smart 	for (;;) {
1607475d0fe7SJames Smart 		nport = list_first_entry_or_null(&fcloop_nports,
1608475d0fe7SJames Smart 						typeof(*nport), nport_list);
1609475d0fe7SJames Smart 		if (!nport)
1610475d0fe7SJames Smart 			break;
1611475d0fe7SJames Smart 
1612475d0fe7SJames Smart 		tport = __unlink_target_port(nport);
1613475d0fe7SJames Smart 		rport = __unlink_remote_port(nport);
1614475d0fe7SJames Smart 
1615475d0fe7SJames Smart 		spin_unlock_irqrestore(&fcloop_lock, flags);
1616475d0fe7SJames Smart 
1617fddc9923SJames Smart 		ret = __targetport_unreg(nport, tport);
1618475d0fe7SJames Smart 		if (ret)
1619475d0fe7SJames Smart 			pr_warn("%s: Failed deleting target port\n", __func__);
1620475d0fe7SJames Smart 
1621fddc9923SJames Smart 		ret = __remoteport_unreg(nport, rport);
1622475d0fe7SJames Smart 		if (ret)
1623475d0fe7SJames Smart 			pr_warn("%s: Failed deleting remote port\n", __func__);
1624475d0fe7SJames Smart 
1625475d0fe7SJames Smart 		spin_lock_irqsave(&fcloop_lock, flags);
1626475d0fe7SJames Smart 	}
1627475d0fe7SJames Smart 
1628475d0fe7SJames Smart 	for (;;) {
1629475d0fe7SJames Smart 		lport = list_first_entry_or_null(&fcloop_lports,
1630475d0fe7SJames Smart 						typeof(*lport), lport_list);
1631475d0fe7SJames Smart 		if (!lport)
1632475d0fe7SJames Smart 			break;
1633475d0fe7SJames Smart 
1634475d0fe7SJames Smart 		__unlink_local_port(lport);
1635475d0fe7SJames Smart 
1636475d0fe7SJames Smart 		spin_unlock_irqrestore(&fcloop_lock, flags);
1637475d0fe7SJames Smart 
1638475d0fe7SJames Smart 		ret = __wait_localport_unreg(lport);
1639475d0fe7SJames Smart 		if (ret)
1640475d0fe7SJames Smart 			pr_warn("%s: Failed deleting local port\n", __func__);
1641475d0fe7SJames Smart 
1642475d0fe7SJames Smart 		spin_lock_irqsave(&fcloop_lock, flags);
1643475d0fe7SJames Smart 	}
1644475d0fe7SJames Smart 
1645475d0fe7SJames Smart 	spin_unlock_irqrestore(&fcloop_lock, flags);
1646475d0fe7SJames Smart 
1647475d0fe7SJames Smart 	put_device(fcloop_device);
1648475d0fe7SJames Smart 
1649475d0fe7SJames Smart 	device_destroy(fcloop_class, MKDEV(0, 0));
1650475d0fe7SJames Smart 	class_destroy(fcloop_class);
1651475d0fe7SJames Smart }
1652475d0fe7SJames Smart 
1653475d0fe7SJames Smart module_init(fcloop_init);
1654475d0fe7SJames Smart module_exit(fcloop_exit);
1655475d0fe7SJames Smart 
1656475d0fe7SJames Smart MODULE_LICENSE("GPL v2");
1657