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