1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2021 Broadcom. All Rights Reserved. The term |
4 | * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. |
5 | */ |
6 | |
7 | #include "efct_driver.h" |
8 | #include "efct_unsol.h" |
9 | |
10 | #define frame_printf(efct, hdr, fmt, ...) \ |
11 | do { \ |
12 | char s_id_text[16]; \ |
13 | efc_node_fcid_display(ntoh24((hdr)->fh_s_id), \ |
14 | s_id_text, sizeof(s_id_text)); \ |
15 | efc_log_debug(efct, "[%06x.%s] %02x/%04x/%04x: " fmt, \ |
16 | ntoh24((hdr)->fh_d_id), s_id_text, \ |
17 | (hdr)->fh_r_ctl, be16_to_cpu((hdr)->fh_ox_id), \ |
18 | be16_to_cpu((hdr)->fh_rx_id), ##__VA_ARGS__); \ |
19 | } while (0) |
20 | |
21 | static struct efct_node * |
22 | efct_node_find(struct efct *efct, u32 port_id, u32 node_id) |
23 | { |
24 | struct efct_node *node; |
25 | u64 id = (u64)port_id << 32 | node_id; |
26 | |
27 | /* |
28 | * During node shutdown, Lookup will be removed first, |
29 | * before announcing to backend. So, no new IOs will be allowed |
30 | */ |
31 | /* Find a target node, given s_id and d_id */ |
32 | node = xa_load(&efct->lookup, index: id); |
33 | if (node) |
34 | kref_get(kref: &node->ref); |
35 | |
36 | return node; |
37 | } |
38 | |
39 | static int |
40 | efct_dispatch_frame(struct efct *efct, struct efc_hw_sequence *seq) |
41 | { |
42 | struct efct_node *node; |
43 | struct fc_frame_header *hdr; |
44 | u32 s_id, d_id; |
45 | |
46 | hdr = seq->header->dma.virt; |
47 | |
48 | /* extract the s_id and d_id */ |
49 | s_id = ntoh24(p: hdr->fh_s_id); |
50 | d_id = ntoh24(p: hdr->fh_d_id); |
51 | |
52 | if (!(hdr->fh_type == FC_TYPE_FCP || hdr->fh_type == FC_TYPE_BLS)) |
53 | return -EIO; |
54 | |
55 | if (hdr->fh_type == FC_TYPE_FCP) { |
56 | node = efct_node_find(efct, port_id: d_id, node_id: s_id); |
57 | if (!node) { |
58 | efc_log_err(efct, |
59 | "Node not found, drop cmd d_id:%x s_id:%x\n" , |
60 | d_id, s_id); |
61 | efct_hw_sequence_free(hw: &efct->hw, seq); |
62 | return 0; |
63 | } |
64 | |
65 | efct_dispatch_fcp_cmd(node, seq); |
66 | } else { |
67 | node = efct_node_find(efct, port_id: d_id, node_id: s_id); |
68 | if (!node) { |
69 | efc_log_err(efct, "ABTS: Node not found, d_id:%x s_id:%x\n" , |
70 | d_id, s_id); |
71 | return -EIO; |
72 | } |
73 | |
74 | efc_log_err(efct, "Received ABTS for Node:%p\n" , node); |
75 | efct_node_recv_abts_frame(node, seq); |
76 | } |
77 | |
78 | kref_put(kref: &node->ref, release: node->release); |
79 | efct_hw_sequence_free(hw: &efct->hw, seq); |
80 | return 0; |
81 | } |
82 | |
83 | int |
84 | efct_unsolicited_cb(void *arg, struct efc_hw_sequence *seq) |
85 | { |
86 | struct efct *efct = arg; |
87 | |
88 | /* Process FCP command */ |
89 | if (!efct_dispatch_frame(efct, seq)) |
90 | return 0; |
91 | |
92 | /* Forward frame to discovery lib */ |
93 | efc_dispatch_frame(efc: efct->efcport, seq); |
94 | return 0; |
95 | } |
96 | |
97 | static int |
98 | efct_fc_tmf_rejected_cb(struct efct_io *io, |
99 | enum efct_scsi_io_status scsi_status, |
100 | u32 flags, void *arg) |
101 | { |
102 | efct_scsi_io_free(io); |
103 | return 0; |
104 | } |
105 | |
106 | static void |
107 | efct_dispatch_unsol_tmf(struct efct_io *io, u8 tm_flags, u32 lun) |
108 | { |
109 | u32 i; |
110 | struct { |
111 | u32 mask; |
112 | enum efct_scsi_tmf_cmd cmd; |
113 | } tmflist[] = { |
114 | {FCP_TMF_ABT_TASK_SET, EFCT_SCSI_TMF_ABORT_TASK_SET}, |
115 | {FCP_TMF_CLR_TASK_SET, EFCT_SCSI_TMF_CLEAR_TASK_SET}, |
116 | {FCP_TMF_LUN_RESET, EFCT_SCSI_TMF_LOGICAL_UNIT_RESET}, |
117 | {FCP_TMF_TGT_RESET, EFCT_SCSI_TMF_TARGET_RESET}, |
118 | {FCP_TMF_CLR_ACA, EFCT_SCSI_TMF_CLEAR_ACA} }; |
119 | |
120 | io->exp_xfer_len = 0; |
121 | |
122 | for (i = 0; i < ARRAY_SIZE(tmflist); i++) { |
123 | if (tmflist[i].mask & tm_flags) { |
124 | io->tmf_cmd = tmflist[i].cmd; |
125 | efct_scsi_recv_tmf(tmfio: io, lun, cmd: tmflist[i].cmd, NULL, flags: 0); |
126 | break; |
127 | } |
128 | } |
129 | if (i == ARRAY_SIZE(tmflist)) { |
130 | /* Not handled */ |
131 | efc_log_err(io->node->efct, "TMF x%x rejected\n" , tm_flags); |
132 | efct_scsi_send_tmf_resp(io, rspcode: EFCT_SCSI_TMF_FUNCTION_REJECTED, |
133 | NULL, cb: efct_fc_tmf_rejected_cb, NULL); |
134 | } |
135 | } |
136 | |
137 | static int |
138 | efct_validate_fcp_cmd(struct efct *efct, struct efc_hw_sequence *seq) |
139 | { |
140 | /* |
141 | * If we received less than FCP_CMND_IU bytes, assume that the frame is |
142 | * corrupted in some way and drop it. |
143 | * This was seen when jamming the FCTL |
144 | * fill bytes field. |
145 | */ |
146 | if (seq->payload->dma.len < sizeof(struct fcp_cmnd)) { |
147 | struct fc_frame_header *fchdr = seq->header->dma.virt; |
148 | |
149 | efc_log_debug(efct, |
150 | "drop ox_id %04x payload (%zd) less than (%zd)\n" , |
151 | be16_to_cpu(fchdr->fh_ox_id), |
152 | seq->payload->dma.len, sizeof(struct fcp_cmnd)); |
153 | return -EIO; |
154 | } |
155 | return 0; |
156 | } |
157 | |
158 | static void |
159 | efct_populate_io_fcp_cmd(struct efct_io *io, struct fcp_cmnd *cmnd, |
160 | struct fc_frame_header *fchdr, bool sit) |
161 | { |
162 | io->init_task_tag = be16_to_cpu(fchdr->fh_ox_id); |
163 | /* note, tgt_task_tag, hw_tag set when HW io is allocated */ |
164 | io->exp_xfer_len = be32_to_cpu(cmnd->fc_dl); |
165 | io->transferred = 0; |
166 | |
167 | /* The upper 7 bits of CS_CTL is the frame priority thru the SAN. |
168 | * Our assertion here is, the priority given to a frame containing |
169 | * the FCP cmd should be the priority given to ALL frames contained |
170 | * in that IO. Thus we need to save the incoming CS_CTL here. |
171 | */ |
172 | if (ntoh24(p: fchdr->fh_f_ctl) & FC_FC_RES_B17) |
173 | io->cs_ctl = fchdr->fh_cs_ctl; |
174 | else |
175 | io->cs_ctl = 0; |
176 | |
177 | io->seq_init = sit; |
178 | } |
179 | |
180 | static u32 |
181 | efct_get_flags_fcp_cmd(struct fcp_cmnd *cmnd) |
182 | { |
183 | u32 flags = 0; |
184 | |
185 | switch (cmnd->fc_pri_ta & FCP_PTA_MASK) { |
186 | case FCP_PTA_SIMPLE: |
187 | flags |= EFCT_SCSI_CMD_SIMPLE; |
188 | break; |
189 | case FCP_PTA_HEADQ: |
190 | flags |= EFCT_SCSI_CMD_HEAD_OF_QUEUE; |
191 | break; |
192 | case FCP_PTA_ORDERED: |
193 | flags |= EFCT_SCSI_CMD_ORDERED; |
194 | break; |
195 | case FCP_PTA_ACA: |
196 | flags |= EFCT_SCSI_CMD_ACA; |
197 | break; |
198 | } |
199 | if (cmnd->fc_flags & FCP_CFL_WRDATA) |
200 | flags |= EFCT_SCSI_CMD_DIR_IN; |
201 | if (cmnd->fc_flags & FCP_CFL_RDDATA) |
202 | flags |= EFCT_SCSI_CMD_DIR_OUT; |
203 | |
204 | return flags; |
205 | } |
206 | |
207 | static void |
208 | efct_sframe_common_send_cb(void *arg, u8 *cqe, int status) |
209 | { |
210 | struct efct_hw_send_frame_context *ctx = arg; |
211 | struct efct_hw *hw = ctx->hw; |
212 | |
213 | /* Free WQ completion callback */ |
214 | efct_hw_reqtag_free(hw, wqcb: ctx->wqcb); |
215 | |
216 | /* Free sequence */ |
217 | efct_hw_sequence_free(hw, seq: ctx->seq); |
218 | } |
219 | |
220 | static int |
221 | efct_sframe_common_send(struct efct_node *node, |
222 | struct efc_hw_sequence *seq, |
223 | enum fc_rctl r_ctl, u32 f_ctl, |
224 | u8 type, void *payload, u32 payload_len) |
225 | { |
226 | struct efct *efct = node->efct; |
227 | struct efct_hw *hw = &efct->hw; |
228 | int rc = 0; |
229 | struct fc_frame_header *req_hdr = seq->header->dma.virt; |
230 | struct fc_frame_header hdr; |
231 | struct efct_hw_send_frame_context *ctx; |
232 | |
233 | u32 heap_size = seq->payload->dma.size; |
234 | uintptr_t heap_phys_base = seq->payload->dma.phys; |
235 | u8 *heap_virt_base = seq->payload->dma.virt; |
236 | u32 heap_offset = 0; |
237 | |
238 | /* Build the FC header reusing the RQ header DMA buffer */ |
239 | memset(&hdr, 0, sizeof(hdr)); |
240 | hdr.fh_r_ctl = r_ctl; |
241 | /* send it back to whomever sent it to us */ |
242 | memcpy(hdr.fh_d_id, req_hdr->fh_s_id, sizeof(hdr.fh_d_id)); |
243 | memcpy(hdr.fh_s_id, req_hdr->fh_d_id, sizeof(hdr.fh_s_id)); |
244 | hdr.fh_type = type; |
245 | hton24(p: hdr.fh_f_ctl, v: f_ctl); |
246 | hdr.fh_ox_id = req_hdr->fh_ox_id; |
247 | hdr.fh_rx_id = req_hdr->fh_rx_id; |
248 | hdr.fh_cs_ctl = 0; |
249 | hdr.fh_df_ctl = 0; |
250 | hdr.fh_seq_cnt = 0; |
251 | hdr.fh_parm_offset = 0; |
252 | |
253 | /* |
254 | * send_frame_seq_id is an atomic, we just let it increment, |
255 | * while storing only the low 8 bits to hdr->seq_id |
256 | */ |
257 | hdr.fh_seq_id = (u8)atomic_add_return(i: 1, v: &hw->send_frame_seq_id); |
258 | hdr.fh_seq_id--; |
259 | |
260 | /* Allocate and fill in the send frame request context */ |
261 | ctx = (void *)(heap_virt_base + heap_offset); |
262 | heap_offset += sizeof(*ctx); |
263 | if (heap_offset > heap_size) { |
264 | efc_log_err(efct, "Fill send frame failed offset %d size %d\n" , |
265 | heap_offset, heap_size); |
266 | return -EIO; |
267 | } |
268 | |
269 | memset(ctx, 0, sizeof(*ctx)); |
270 | |
271 | /* Save sequence */ |
272 | ctx->seq = seq; |
273 | |
274 | /* Allocate a response payload DMA buffer from the heap */ |
275 | ctx->payload.phys = heap_phys_base + heap_offset; |
276 | ctx->payload.virt = heap_virt_base + heap_offset; |
277 | ctx->payload.size = payload_len; |
278 | ctx->payload.len = payload_len; |
279 | heap_offset += payload_len; |
280 | if (heap_offset > heap_size) { |
281 | efc_log_err(efct, "Fill send frame failed offset %d size %d\n" , |
282 | heap_offset, heap_size); |
283 | return -EIO; |
284 | } |
285 | |
286 | /* Copy the payload in */ |
287 | memcpy(ctx->payload.virt, payload, payload_len); |
288 | |
289 | /* Send */ |
290 | rc = efct_hw_send_frame(hw: &efct->hw, hdr: (void *)&hdr, sof: FC_SOF_N3, |
291 | eof: FC_EOF_T, payload: &ctx->payload, ctx, |
292 | callback: efct_sframe_common_send_cb, arg: ctx); |
293 | if (rc) |
294 | efc_log_debug(efct, "efct_hw_send_frame failed: %d\n" , rc); |
295 | |
296 | return rc; |
297 | } |
298 | |
299 | static int |
300 | efct_sframe_send_fcp_rsp(struct efct_node *node, struct efc_hw_sequence *seq, |
301 | void *rsp, u32 rsp_len) |
302 | { |
303 | return efct_sframe_common_send(node, seq, r_ctl: FC_RCTL_DD_CMD_STATUS, |
304 | FC_FC_EX_CTX | |
305 | FC_FC_LAST_SEQ | |
306 | FC_FC_END_SEQ | |
307 | FC_FC_SEQ_INIT, |
308 | type: FC_TYPE_FCP, |
309 | payload: rsp, payload_len: rsp_len); |
310 | } |
311 | |
312 | static int |
313 | efct_sframe_send_task_set_full_or_busy(struct efct_node *node, |
314 | struct efc_hw_sequence *seq) |
315 | { |
316 | struct fcp_resp_with_ext fcprsp; |
317 | struct fcp_cmnd *fcpcmd = seq->payload->dma.virt; |
318 | int rc = 0; |
319 | unsigned long flags = 0; |
320 | struct efct *efct = node->efct; |
321 | |
322 | /* construct task set full or busy response */ |
323 | memset(&fcprsp, 0, sizeof(fcprsp)); |
324 | spin_lock_irqsave(&node->active_ios_lock, flags); |
325 | fcprsp.resp.fr_status = list_empty(head: &node->active_ios) ? |
326 | SAM_STAT_BUSY : SAM_STAT_TASK_SET_FULL; |
327 | spin_unlock_irqrestore(lock: &node->active_ios_lock, flags); |
328 | *((u32 *)&fcprsp.ext.fr_resid) = be32_to_cpu(fcpcmd->fc_dl); |
329 | |
330 | /* send it using send_frame */ |
331 | rc = efct_sframe_send_fcp_rsp(node, seq, rsp: &fcprsp, rsp_len: sizeof(fcprsp)); |
332 | if (rc) |
333 | efc_log_debug(efct, "efct_sframe_send_fcp_rsp failed %d\n" , rc); |
334 | |
335 | return rc; |
336 | } |
337 | |
338 | int |
339 | efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq) |
340 | { |
341 | struct efct *efct = node->efct; |
342 | struct fc_frame_header *fchdr = seq->header->dma.virt; |
343 | struct fcp_cmnd *cmnd = NULL; |
344 | struct efct_io *io = NULL; |
345 | u32 lun; |
346 | |
347 | if (!seq->payload) { |
348 | efc_log_err(efct, "Sequence payload is NULL.\n" ); |
349 | return -EIO; |
350 | } |
351 | |
352 | cmnd = seq->payload->dma.virt; |
353 | |
354 | /* perform FCP_CMND validation check(s) */ |
355 | if (efct_validate_fcp_cmd(efct, seq)) |
356 | return -EIO; |
357 | |
358 | lun = scsilun_to_int(&cmnd->fc_lun); |
359 | if (lun == U32_MAX) |
360 | return -EIO; |
361 | |
362 | io = efct_scsi_io_alloc(node); |
363 | if (!io) { |
364 | int rc; |
365 | |
366 | /* Use SEND_FRAME to send task set full or busy */ |
367 | rc = efct_sframe_send_task_set_full_or_busy(node, seq); |
368 | if (rc) |
369 | efc_log_err(efct, "Failed to send busy task: %d\n" , rc); |
370 | |
371 | return rc; |
372 | } |
373 | |
374 | io->hw_priv = seq->hw_priv; |
375 | |
376 | io->app_id = 0; |
377 | |
378 | /* RQ pair, if we got here, SIT=1 */ |
379 | efct_populate_io_fcp_cmd(io, cmnd, fchdr, sit: true); |
380 | |
381 | if (cmnd->fc_tm_flags) { |
382 | efct_dispatch_unsol_tmf(io, tm_flags: cmnd->fc_tm_flags, lun); |
383 | } else { |
384 | u32 flags = efct_get_flags_fcp_cmd(cmnd); |
385 | |
386 | if (cmnd->fc_flags & FCP_CFL_LEN_MASK) { |
387 | efc_log_err(efct, "Additional CDB not supported\n" ); |
388 | return -EIO; |
389 | } |
390 | /* |
391 | * Can return failure for things like task set full and UAs, |
392 | * no need to treat as a dropped frame if rc != 0 |
393 | */ |
394 | efct_scsi_recv_cmd(io, lun, cdb: cmnd->fc_cdb, |
395 | cdb_len: sizeof(cmnd->fc_cdb), flags); |
396 | } |
397 | |
398 | return 0; |
399 | } |
400 | |
401 | static int |
402 | efct_process_abts(struct efct_io *io, struct fc_frame_header *hdr) |
403 | { |
404 | struct efct_node *node = io->node; |
405 | struct efct *efct = io->efct; |
406 | u16 ox_id = be16_to_cpu(hdr->fh_ox_id); |
407 | u16 rx_id = be16_to_cpu(hdr->fh_rx_id); |
408 | struct efct_io *abortio; |
409 | |
410 | /* Find IO and attempt to take a reference on it */ |
411 | abortio = efct_io_find_tgt_io(efct, node, ox_id, rx_id); |
412 | |
413 | if (abortio) { |
414 | /* Got a reference on the IO. Hold it until backend |
415 | * is notified below |
416 | */ |
417 | efc_log_info(node->efct, "Abort ox_id [%04x] rx_id [%04x]\n" , |
418 | ox_id, rx_id); |
419 | |
420 | /* |
421 | * Save the ox_id for the ABTS as the init_task_tag in our |
422 | * manufactured |
423 | * TMF IO object |
424 | */ |
425 | io->display_name = "abts" ; |
426 | io->init_task_tag = ox_id; |
427 | /* don't set tgt_task_tag, don't want to confuse with XRI */ |
428 | |
429 | /* |
430 | * Save the rx_id from the ABTS as it is |
431 | * needed for the BLS response, |
432 | * regardless of the IO context's rx_id |
433 | */ |
434 | io->abort_rx_id = rx_id; |
435 | |
436 | /* Call target server command abort */ |
437 | io->tmf_cmd = EFCT_SCSI_TMF_ABORT_TASK; |
438 | efct_scsi_recv_tmf(tmfio: io, lun: abortio->tgt_io.lun, |
439 | cmd: EFCT_SCSI_TMF_ABORT_TASK, abortio, flags: 0); |
440 | |
441 | /* |
442 | * Backend will have taken an additional |
443 | * reference on the IO if needed; |
444 | * done with current reference. |
445 | */ |
446 | kref_put(kref: &abortio->ref, release: abortio->release); |
447 | } else { |
448 | /* |
449 | * Either IO was not found or it has been |
450 | * freed between finding it |
451 | * and attempting to get the reference, |
452 | */ |
453 | efc_log_info(node->efct, "Abort: ox_id [%04x], IO not found\n" , |
454 | ox_id); |
455 | |
456 | /* Send a BA_RJT */ |
457 | efct_bls_send_rjt(io, hdr); |
458 | } |
459 | return 0; |
460 | } |
461 | |
462 | int |
463 | efct_node_recv_abts_frame(struct efct_node *node, struct efc_hw_sequence *seq) |
464 | { |
465 | struct efct *efct = node->efct; |
466 | struct fc_frame_header *hdr = seq->header->dma.virt; |
467 | struct efct_io *io = NULL; |
468 | |
469 | node->abort_cnt++; |
470 | io = efct_scsi_io_alloc(node); |
471 | if (io) { |
472 | io->hw_priv = seq->hw_priv; |
473 | /* If we got this far, SIT=1 */ |
474 | io->seq_init = 1; |
475 | |
476 | /* fill out generic fields */ |
477 | io->efct = efct; |
478 | io->node = node; |
479 | io->cmd_tgt = true; |
480 | |
481 | efct_process_abts(io, hdr: seq->header->dma.virt); |
482 | } else { |
483 | efc_log_err(efct, |
484 | "SCSI IO allocation failed for ABTS received " ); |
485 | efc_log_err(efct, "s_id %06x d_id %06x ox_id %04x rx_id %04x\n" , |
486 | ntoh24(hdr->fh_s_id), ntoh24(hdr->fh_d_id), |
487 | be16_to_cpu(hdr->fh_ox_id), |
488 | be16_to_cpu(hdr->fh_rx_id)); |
489 | } |
490 | |
491 | return 0; |
492 | } |
493 | |