Commit | Line | Data |
---|---|---|
3699d92a KP |
1 | /* |
2 | * Copyright (c) 2010 Cisco Systems, Inc. | |
3 | * | |
4 | * Portions based on tcm_loop_fabric_scsi.c and libfc/fc_fcp.c | |
5 | * | |
6 | * Copyright (c) 2007 Intel Corporation. All rights reserved. | |
7 | * Copyright (c) 2008 Red Hat, Inc. All rights reserved. | |
8 | * Copyright (c) 2008 Mike Christie | |
9 | * Copyright (c) 2009 Rising Tide, Inc. | |
10 | * Copyright (c) 2009 Linux-iSCSI.org | |
11 | * Copyright (c) 2009 Nicholas A. Bellinger <nab@linux-iscsi.org> | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify it | |
14 | * under the terms and conditions of the GNU General Public License, | |
15 | * version 2, as published by the Free Software Foundation. | |
16 | * | |
17 | * This program is distributed in the hope it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
20 | * more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License along with | |
23 | * this program; if not, write to the Free Software Foundation, Inc., | |
24 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
25 | */ | |
26 | ||
27 | /* XXX TBD some includes may be extraneous */ | |
28 | ||
29 | #include <linux/module.h> | |
30 | #include <linux/moduleparam.h> | |
3699d92a KP |
31 | #include <linux/utsname.h> |
32 | #include <linux/init.h> | |
33 | #include <linux/slab.h> | |
34 | #include <linux/kthread.h> | |
35 | #include <linux/types.h> | |
36 | #include <linux/string.h> | |
37 | #include <linux/configfs.h> | |
38 | #include <linux/ctype.h> | |
39 | #include <linux/hash.h> | |
6708bb27 | 40 | #include <linux/ratelimit.h> |
3699d92a | 41 | #include <asm/unaligned.h> |
3699d92a KP |
42 | #include <scsi/libfc.h> |
43 | #include <scsi/fc_encode.h> | |
44 | ||
45 | #include <target/target_core_base.h> | |
c4795fb2 | 46 | #include <target/target_core_fabric.h> |
3699d92a KP |
47 | |
48 | #include "tcm_fc.h" | |
49 | ||
50 | /* | |
51 | * Deliver read data back to initiator. | |
52 | * XXX TBD handle resource problems later. | |
53 | */ | |
54 | int ft_queue_data_in(struct se_cmd *se_cmd) | |
55 | { | |
56 | struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); | |
3699d92a KP |
57 | struct fc_frame *fp = NULL; |
58 | struct fc_exch *ep; | |
59 | struct fc_lport *lport; | |
ec98f782 | 60 | struct scatterlist *sg = NULL; |
3699d92a KP |
61 | size_t remaining; |
62 | u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF; | |
ec98f782 | 63 | u32 mem_off = 0; |
3699d92a KP |
64 | u32 fh_off = 0; |
65 | u32 frame_off = 0; | |
66 | size_t frame_len = 0; | |
ec98f782 | 67 | size_t mem_len = 0; |
3699d92a KP |
68 | size_t tlen; |
69 | size_t off_in_page; | |
ec98f782 | 70 | struct page *page = NULL; |
3699d92a KP |
71 | int use_sg; |
72 | int error; | |
73 | void *page_addr; | |
74 | void *from; | |
75 | void *to = NULL; | |
76 | ||
e1c40382 MR |
77 | if (cmd->aborted) |
78 | return 0; | |
b3e5fe16 NB |
79 | |
80 | if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL) | |
81 | goto queue_status; | |
82 | ||
3699d92a KP |
83 | ep = fc_seq_exch(cmd->seq); |
84 | lport = ep->lp; | |
85 | cmd->seq = lport->tt.seq_start_next(cmd->seq); | |
86 | ||
3699d92a KP |
87 | remaining = se_cmd->data_length; |
88 | ||
89 | /* | |
05d1c7c0 | 90 | * Setup to use first mem list entry, unless no data. |
3699d92a | 91 | */ |
ec98f782 | 92 | BUG_ON(remaining && !se_cmd->t_data_sg); |
05d1c7c0 | 93 | if (remaining) { |
ec98f782 AG |
94 | sg = se_cmd->t_data_sg; |
95 | mem_len = sg->length; | |
96 | mem_off = sg->offset; | |
97 | page = sg_page(sg); | |
3699d92a KP |
98 | } |
99 | ||
100 | /* no scatter/gather in skb for odd word length due to fc_seq_send() */ | |
101 | use_sg = !(remaining % 4); | |
102 | ||
103 | while (remaining) { | |
d3682b1a MR |
104 | struct fc_seq *seq = cmd->seq; |
105 | ||
106 | if (!seq) { | |
107 | pr_debug("%s: Command aborted, xid 0x%x\n", | |
108 | __func__, ep->xid); | |
109 | break; | |
110 | } | |
3699d92a | 111 | if (!mem_len) { |
ec98f782 AG |
112 | sg = sg_next(sg); |
113 | mem_len = min((size_t)sg->length, remaining); | |
114 | mem_off = sg->offset; | |
115 | page = sg_page(sg); | |
3699d92a KP |
116 | } |
117 | if (!frame_len) { | |
118 | /* | |
119 | * If lport's has capability of Large Send Offload LSO) | |
120 | * , then allow 'frame_len' to be as big as 'lso_max' | |
121 | * if indicated transfer length is >= lport->lso_max | |
122 | */ | |
123 | frame_len = (lport->seq_offload) ? lport->lso_max : | |
124 | cmd->sess->max_frame; | |
125 | frame_len = min(frame_len, remaining); | |
126 | fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len); | |
127 | if (!fp) | |
128 | return -ENOMEM; | |
129 | to = fc_frame_payload_get(fp, 0); | |
130 | fh_off = frame_off; | |
131 | frame_off += frame_len; | |
132 | /* | |
133 | * Setup the frame's max payload which is used by base | |
134 | * driver to indicate HW about max frame size, so that | |
135 | * HW can do fragmentation appropriately based on | |
136 | * "gso_max_size" of underline netdev. | |
137 | */ | |
138 | fr_max_payload(fp) = cmd->sess->max_frame; | |
139 | } | |
140 | tlen = min(mem_len, frame_len); | |
141 | ||
142 | if (use_sg) { | |
05d1c7c0 | 143 | off_in_page = mem_off; |
3699d92a KP |
144 | BUG_ON(!page); |
145 | get_page(page); | |
146 | skb_fill_page_desc(fp_skb(fp), | |
147 | skb_shinfo(fp_skb(fp))->nr_frags, | |
148 | page, off_in_page, tlen); | |
149 | fr_len(fp) += tlen; | |
150 | fp_skb(fp)->data_len += tlen; | |
151 | fp_skb(fp)->truesize += | |
152 | PAGE_SIZE << compound_order(page); | |
05d1c7c0 | 153 | } else { |
3699d92a | 154 | BUG_ON(!page); |
ca747d61 | 155 | from = kmap_atomic(page + (mem_off >> PAGE_SHIFT)); |
3699d92a KP |
156 | page_addr = from; |
157 | from += mem_off & ~PAGE_MASK; | |
158 | tlen = min(tlen, (size_t)(PAGE_SIZE - | |
159 | (mem_off & ~PAGE_MASK))); | |
160 | memcpy(to, from, tlen); | |
ca747d61 | 161 | kunmap_atomic(page_addr); |
3699d92a | 162 | to += tlen; |
3699d92a KP |
163 | } |
164 | ||
165 | mem_off += tlen; | |
166 | mem_len -= tlen; | |
167 | frame_len -= tlen; | |
168 | remaining -= tlen; | |
169 | ||
170 | if (frame_len && | |
171 | (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN)) | |
172 | continue; | |
173 | if (!remaining) | |
174 | f_ctl |= FC_FC_END_SEQ; | |
175 | fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, | |
176 | FC_TYPE_FCP, f_ctl, fh_off); | |
d3682b1a | 177 | error = lport->tt.seq_send(lport, seq, fp); |
3699d92a | 178 | if (error) { |
b3e5fe16 | 179 | pr_info_ratelimited("%s: Failed to send frame %p, " |
95efa286 | 180 | "xid <0x%x>, remaining %zu, " |
3699d92a KP |
181 | "lso_max <0x%x>\n", |
182 | __func__, fp, ep->xid, | |
183 | remaining, lport->lso_max); | |
b3e5fe16 NB |
184 | /* |
185 | * Go ahead and set TASK_SET_FULL status ignoring the | |
186 | * rest of the DataIN, and immediately attempt to | |
187 | * send the response via ft_queue_status() in order | |
188 | * to notify the initiator that it should reduce it's | |
189 | * per LUN queue_depth. | |
190 | */ | |
191 | se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; | |
192 | break; | |
3699d92a KP |
193 | } |
194 | } | |
b3e5fe16 | 195 | queue_status: |
3699d92a KP |
196 | return ft_queue_status(se_cmd); |
197 | } | |
198 | ||
b8b22533 CH |
199 | static void ft_execute_work(struct work_struct *work) |
200 | { | |
201 | struct ft_cmd *cmd = container_of(work, struct ft_cmd, work); | |
202 | ||
203 | target_execute_cmd(&cmd->se_cmd); | |
204 | } | |
205 | ||
3699d92a KP |
206 | /* |
207 | * Receive write data frame. | |
208 | */ | |
209 | void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp) | |
210 | { | |
211 | struct se_cmd *se_cmd = &cmd->se_cmd; | |
212 | struct fc_seq *seq = cmd->seq; | |
213 | struct fc_exch *ep; | |
214 | struct fc_lport *lport; | |
3699d92a | 215 | struct fc_frame_header *fh; |
ec98f782 AG |
216 | struct scatterlist *sg = NULL; |
217 | u32 mem_off = 0; | |
3699d92a KP |
218 | u32 rel_off; |
219 | size_t frame_len; | |
ec98f782 | 220 | size_t mem_len = 0; |
3699d92a | 221 | size_t tlen; |
ec98f782 | 222 | struct page *page = NULL; |
3699d92a KP |
223 | void *page_addr; |
224 | void *from; | |
225 | void *to; | |
226 | u32 f_ctl; | |
227 | void *buf; | |
228 | ||
3699d92a KP |
229 | fh = fc_frame_header_get(fp); |
230 | if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF)) | |
231 | goto drop; | |
232 | ||
dcd998cc KP |
233 | f_ctl = ntoh24(fh->fh_f_ctl); |
234 | ep = fc_seq_exch(seq); | |
235 | lport = ep->lp; | |
236 | if (cmd->was_ddp_setup) { | |
237 | BUG_ON(!ep); | |
238 | BUG_ON(!lport); | |
079587b4 KP |
239 | /* |
240 | * Since DDP (Large Rx offload) was setup for this request, | |
241 | * payload is expected to be copied directly to user buffers. | |
242 | */ | |
243 | buf = fc_frame_payload_get(fp, 1); | |
244 | if (buf) | |
245 | pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, " | |
dcd998cc KP |
246 | "cmd->sg_cnt 0x%x. DDP was setup" |
247 | " hence not expected to receive frame with " | |
079587b4 KP |
248 | "payload, Frame will be dropped if" |
249 | "'Sequence Initiative' bit in f_ctl is" | |
dcd998cc | 250 | "not set\n", __func__, ep->xid, f_ctl, |
e182d682 | 251 | se_cmd->t_data_sg, se_cmd->t_data_nents); |
079587b4 KP |
252 | /* |
253 | * Invalidate HW DDP context if it was setup for respective | |
254 | * command. Invalidation of HW DDP context is requited in both | |
255 | * situation (success and error). | |
256 | */ | |
257 | ft_invl_hw_context(cmd); | |
3699d92a | 258 | |
079587b4 KP |
259 | /* |
260 | * If "Sequence Initiative (TSI)" bit set in f_ctl, means last | |
261 | * write data frame is received successfully where payload is | |
262 | * posted directly to user buffer and only the last frame's | |
263 | * header is posted in receive queue. | |
264 | * | |
265 | * If "Sequence Initiative (TSI)" bit is not set, means error | |
266 | * condition w.r.t. DDP, hence drop the packet and let explict | |
267 | * ABORTS from other end of exchange timer trigger the recovery. | |
268 | */ | |
269 | if (f_ctl & FC_FC_SEQ_INIT) | |
270 | goto last_frame; | |
271 | else | |
272 | goto drop; | |
273 | } | |
3699d92a KP |
274 | |
275 | rel_off = ntohl(fh->fh_parm_offset); | |
276 | frame_len = fr_len(fp); | |
277 | if (frame_len <= sizeof(*fh)) | |
278 | goto drop; | |
279 | frame_len -= sizeof(*fh); | |
280 | from = fc_frame_payload_get(fp, 0); | |
281 | if (rel_off >= se_cmd->data_length) | |
282 | goto drop; | |
283 | if (frame_len + rel_off > se_cmd->data_length) | |
284 | frame_len = se_cmd->data_length - rel_off; | |
285 | ||
286 | /* | |
05d1c7c0 | 287 | * Setup to use first mem list entry, unless no data. |
3699d92a | 288 | */ |
ec98f782 | 289 | BUG_ON(frame_len && !se_cmd->t_data_sg); |
05d1c7c0 | 290 | if (frame_len) { |
ec98f782 AG |
291 | sg = se_cmd->t_data_sg; |
292 | mem_len = sg->length; | |
293 | mem_off = sg->offset; | |
294 | page = sg_page(sg); | |
3699d92a KP |
295 | } |
296 | ||
297 | while (frame_len) { | |
298 | if (!mem_len) { | |
ec98f782 AG |
299 | sg = sg_next(sg); |
300 | mem_len = sg->length; | |
301 | mem_off = sg->offset; | |
302 | page = sg_page(sg); | |
3699d92a KP |
303 | } |
304 | if (rel_off >= mem_len) { | |
305 | rel_off -= mem_len; | |
306 | mem_len = 0; | |
307 | continue; | |
308 | } | |
309 | mem_off += rel_off; | |
310 | mem_len -= rel_off; | |
311 | rel_off = 0; | |
312 | ||
313 | tlen = min(mem_len, frame_len); | |
314 | ||
ca747d61 | 315 | to = kmap_atomic(page + (mem_off >> PAGE_SHIFT)); |
05d1c7c0 AG |
316 | page_addr = to; |
317 | to += mem_off & ~PAGE_MASK; | |
318 | tlen = min(tlen, (size_t)(PAGE_SIZE - | |
319 | (mem_off & ~PAGE_MASK))); | |
320 | memcpy(to, from, tlen); | |
ca747d61 | 321 | kunmap_atomic(page_addr); |
05d1c7c0 | 322 | |
3699d92a KP |
323 | from += tlen; |
324 | frame_len -= tlen; | |
325 | mem_off += tlen; | |
326 | mem_len -= tlen; | |
327 | cmd->write_data_len += tlen; | |
328 | } | |
329 | last_frame: | |
b8b22533 CH |
330 | if (cmd->write_data_len == se_cmd->data_length) { |
331 | INIT_WORK(&cmd->work, ft_execute_work); | |
332 | queue_work(cmd->sess->tport->tpg->workqueue, &cmd->work); | |
333 | } | |
3699d92a KP |
334 | drop: |
335 | fc_frame_free(fp); | |
336 | } | |
dcd998cc KP |
337 | |
338 | /* | |
339 | * Handle and cleanup any HW specific resources if | |
340 | * received ABORTS, errors, timeouts. | |
341 | */ | |
342 | void ft_invl_hw_context(struct ft_cmd *cmd) | |
343 | { | |
7875f179 | 344 | struct fc_seq *seq; |
dcd998cc KP |
345 | struct fc_exch *ep = NULL; |
346 | struct fc_lport *lport = NULL; | |
347 | ||
348 | BUG_ON(!cmd); | |
7875f179 | 349 | seq = cmd->seq; |
dcd998cc KP |
350 | |
351 | /* Cleanup the DDP context in HW if DDP was setup */ | |
352 | if (cmd->was_ddp_setup && seq) { | |
353 | ep = fc_seq_exch(seq); | |
354 | if (ep) { | |
355 | lport = ep->lp; | |
d556546e | 356 | if (lport && (ep->xid <= lport->lro_xid)) { |
dcd998cc KP |
357 | /* |
358 | * "ddp_done" trigger invalidation of HW | |
359 | * specific DDP context | |
360 | */ | |
361 | cmd->write_data_len = lport->tt.ddp_done(lport, | |
362 | ep->xid); | |
363 | ||
364 | /* | |
365 | * Resetting same variable to indicate HW's | |
366 | * DDP context has been invalidated to avoid | |
367 | * re_invalidation of same context (context is | |
368 | * identified using ep->xid) | |
369 | */ | |
370 | cmd->was_ddp_setup = 0; | |
d556546e | 371 | } |
dcd998cc KP |
372 | } |
373 | } | |
374 | } |