aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/target
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@daterainc.com>2013-08-20 18:38:55 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2013-09-10 19:48:43 -0400
commitcbf031f425fd0b30ff10ba83b612753189a6bbbf (patch)
treedcc5696e342739cf9f8b2d9791b19036025075c7 /drivers/target
parent89c12cc925a7d0982dc53b743a42108acc76aef4 (diff)
target: Add support for EXTENDED_COPY copy offload emulation
This patch adds support for EXTENDED_COPY emulation from SPC-3, that enables full copy offload target support within both a single virtual backend device, and across multiple virtual backend devices. It also functions independent of target fabric, and supports copy offload across multiple target fabric ports. This implemenation supports both EXTENDED_COPY PUSH and PULL models of operation, so the actual CDB may be received on either source or desination logical unit. For Target Descriptors, it currently supports the NAA IEEE Registered Extended designator (type 0xe4), which allows the reference of target ports to occur independent of fabric type using EVPD 0x83 WWNs. For Segment Descriptors, it currently supports copy from block to block (0x02) mode. It also honors any present SCSI reservations of the destination target port. Note that only Supports No List Identifier (SNLID=1) mode is supported. Also included is basic RECEIVE_COPY_RESULTS with service action type OPERATING PARAMETERS (0x03) required for SNLID=1 operation. v3 changes: - Fix incorrect return type in target_do_receive_copy_results() (Fengguang) v2 changes: - Use target_alloc_sgl() instead of transport_generic_get_mem() - Convert debug output to use pr_debug() - Convert target_xcopy_parse_target_descriptors() NAA IEEN WWN dump to use 0x%16phN format specification - Drop unnecessary xcopy_pt_cmd->xpt_passthrough_wsem, and associated usage in xcopy_pt_write_pending() and target_xcopy_issue_pt_cmd() - Add check for unsupported EXTENDED_COPY(LID4) service action bits in target_do_xcopy() Cc: Christoph Hellwig <hch@lst.de> Cc: Hannes Reinecke <hare@suse.de> Cc: Martin Petersen <martin.petersen@oracle.com> Cc: Chris Mason <chris.mason@fusionio.com> Cc: Roland Dreier <roland@purestorage.com> Cc: Zach Brown <zab@redhat.com> Cc: James Bottomley <JBottomley@Parallels.com> Cc: Nicholas Bellinger <nab@linux-iscsi.org> Signed-off-by: Nicholas Bellinger <nab@daterainc.com>
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/Makefile3
-rw-r--r--drivers/target/target_core_xcopy.c1082
-rw-r--r--drivers/target/target_core_xcopy.h62
3 files changed, 1146 insertions, 1 deletions
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 9fdcb561422f..85b012d2f89b 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -13,7 +13,8 @@ target_core_mod-y := target_core_configfs.o \
13 target_core_spc.o \ 13 target_core_spc.o \
14 target_core_ua.o \ 14 target_core_ua.o \
15 target_core_rd.o \ 15 target_core_rd.o \
16 target_core_stat.o 16 target_core_stat.o \
17 target_core_xcopy.o
17 18
18obj-$(CONFIG_TARGET_CORE) += target_core_mod.o 19obj-$(CONFIG_TARGET_CORE) += target_core_mod.o
19 20
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
new file mode 100644
index 000000000000..cf566a114911
--- /dev/null
+++ b/drivers/target/target_core_xcopy.c
@@ -0,0 +1,1082 @@
1/*******************************************************************************
2 * Filename: target_core_xcopy.c
3 *
4 * This file contains support for SPC-4 Extended-Copy offload with generic
5 * TCM backends.
6 *
7 * Copyright (c) 2011-2013 Datera, Inc. All rights reserved.
8 *
9 * Author:
10 * Nicholas A. Bellinger <nab@daterainc.com>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 ******************************************************************************/
23
24#include <linux/version.h>
25#include <linux/slab.h>
26#include <linux/spinlock.h>
27#include <linux/list.h>
28#include <linux/configfs.h>
29#include <scsi/scsi.h>
30#include <scsi/scsi_cmnd.h>
31#include <asm/unaligned.h>
32
33#include <target/target_core_base.h>
34#include <target/target_core_backend.h>
35#include <target/target_core_fabric.h>
36#include <target/target_core_configfs.h>
37
38#include "target_core_pr.h"
39#include "target_core_ua.h"
40#include "target_core_xcopy.h"
41
42static struct workqueue_struct *xcopy_wq = NULL;
43/*
44 * From target_core_spc.c
45 */
46extern void spc_parse_naa_6h_vendor_specific(struct se_device *, unsigned char *);
47/*
48 * From target_core_device.c
49 */
50extern struct mutex g_device_mutex;
51extern struct list_head g_device_list;
52/*
53 * From target_core_configfs.c
54 */
55extern struct configfs_subsystem *target_core_subsystem[];
56
57static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf)
58{
59 int off = 0;
60
61 buf[off++] = (0x6 << 4);
62 buf[off++] = 0x01;
63 buf[off++] = 0x40;
64 buf[off] = (0x5 << 4);
65
66 spc_parse_naa_6h_vendor_specific(dev, &buf[off]);
67 return 0;
68}
69
70static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op *xop,
71 bool src)
72{
73 struct se_device *se_dev;
74 struct configfs_subsystem *subsys = target_core_subsystem[0];
75 unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN], *dev_wwn;
76 int rc;
77
78 if (src == true)
79 dev_wwn = &xop->dst_tid_wwn[0];
80 else
81 dev_wwn = &xop->src_tid_wwn[0];
82
83 mutex_lock(&g_device_mutex);
84 list_for_each_entry(se_dev, &g_device_list, g_dev_node) {
85
86 memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
87 target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]);
88
89 rc = memcmp(&tmp_dev_wwn[0], dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
90 if (rc != 0)
91 continue;
92
93 if (src == true) {
94 xop->dst_dev = se_dev;
95 pr_debug("XCOPY 0xe4: Setting xop->dst_dev: %p from located"
96 " se_dev\n", xop->dst_dev);
97 } else {
98 xop->src_dev = se_dev;
99 pr_debug("XCOPY 0xe4: Setting xop->src_dev: %p from located"
100 " se_dev\n", xop->src_dev);
101 }
102
103 rc = configfs_depend_item(subsys,
104 &se_dev->dev_group.cg_item);
105 if (rc != 0) {
106 pr_err("configfs_depend_item attempt failed:"
107 " %d for se_dev: %p\n", rc, se_dev);
108 mutex_unlock(&g_device_mutex);
109 return rc;
110 }
111
112 pr_debug("Called configfs_depend_item for subsys: %p se_dev: %p"
113 " se_dev->se_dev_group: %p\n", subsys, se_dev,
114 &se_dev->dev_group);
115
116 mutex_unlock(&g_device_mutex);
117 return 0;
118 }
119 mutex_unlock(&g_device_mutex);
120
121 pr_err("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
122 return -EINVAL;
123}
124
125static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op *xop,
126 unsigned char *p, bool src)
127{
128 unsigned char *desc = p;
129 unsigned short ript;
130 u8 desig_len;
131 /*
132 * Extract RELATIVE INITIATOR PORT IDENTIFIER
133 */
134 ript = get_unaligned_be16(&desc[2]);
135 pr_debug("XCOPY 0xe4: RELATIVE INITIATOR PORT IDENTIFIER: %hu\n", ript);
136 /*
137 * Check for supported code set, association, and designator type
138 */
139 if ((desc[4] & 0x0f) != 0x1) {
140 pr_err("XCOPY 0xe4: code set of non binary type not supported\n");
141 return -EINVAL;
142 }
143 if ((desc[5] & 0x30) != 0x00) {
144 pr_err("XCOPY 0xe4: association other than LUN not supported\n");
145 return -EINVAL;
146 }
147 if ((desc[5] & 0x0f) != 0x3) {
148 pr_err("XCOPY 0xe4: designator type unsupported: 0x%02x\n",
149 (desc[5] & 0x0f));
150 return -EINVAL;
151 }
152 /*
153 * Check for matching 16 byte length for NAA IEEE Registered Extended
154 * Assigned designator
155 */
156 desig_len = desc[7];
157 if (desig_len != 16) {
158 pr_err("XCOPY 0xe4: invalid desig_len: %d\n", (int)desig_len);
159 return -EINVAL;
160 }
161 pr_debug("XCOPY 0xe4: desig_len: %d\n", (int)desig_len);
162 /*
163 * Check for NAA IEEE Registered Extended Assigned header..
164 */
165 if ((desc[8] & 0xf0) != 0x60) {
166 pr_err("XCOPY 0xe4: Unsupported DESIGNATOR TYPE: 0x%02x\n",
167 (desc[8] & 0xf0));
168 return -EINVAL;
169 }
170
171 if (src == true) {
172 memcpy(&xop->src_tid_wwn[0], &desc[8], XCOPY_NAA_IEEE_REGEX_LEN);
173 /*
174 * Determine if the source designator matches the local device
175 */
176 if (!memcmp(&xop->local_dev_wwn[0], &xop->src_tid_wwn[0],
177 XCOPY_NAA_IEEE_REGEX_LEN)) {
178 xop->op_origin = XCOL_SOURCE_RECV_OP;
179 xop->src_dev = se_cmd->se_dev;
180 pr_debug("XCOPY 0xe4: Set xop->src_dev %p from source"
181 " received xop\n", xop->src_dev);
182 }
183 } else {
184 memcpy(&xop->dst_tid_wwn[0], &desc[8], XCOPY_NAA_IEEE_REGEX_LEN);
185 /*
186 * Determine if the destination designator matches the local device
187 */
188 if (!memcmp(&xop->local_dev_wwn[0], &xop->dst_tid_wwn[0],
189 XCOPY_NAA_IEEE_REGEX_LEN)) {
190 xop->op_origin = XCOL_DEST_RECV_OP;
191 xop->dst_dev = se_cmd->se_dev;
192 pr_debug("XCOPY 0xe4: Set xop->dst_dev: %p from destination"
193 " received xop\n", xop->dst_dev);
194 }
195 }
196
197 return 0;
198}
199
200static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
201 struct xcopy_op *xop, unsigned char *p,
202 unsigned short tdll)
203{
204 struct se_device *local_dev = se_cmd->se_dev;
205 unsigned char *desc = p;
206 int offset = tdll % XCOPY_TARGET_DESC_LEN, rc, ret = 0;
207 unsigned short start = 0;
208 bool src = true;
209
210 if (offset != 0) {
211 pr_err("XCOPY target descriptor list length is not"
212 " multiple of %d\n", XCOPY_TARGET_DESC_LEN);
213 return -EINVAL;
214 }
215 if (tdll > 64) {
216 pr_err("XCOPY target descriptor supports a maximum"
217 " two src/dest descriptors, tdll: %hu too large..\n", tdll);
218 return -EINVAL;
219 }
220 /*
221 * Generate an IEEE Registered Extended designator based upon the
222 * se_device the XCOPY was received upon..
223 */
224 memset(&xop->local_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
225 target_xcopy_gen_naa_ieee(local_dev, &xop->local_dev_wwn[0]);
226
227 while (start < tdll) {
228 /*
229 * Check target descriptor identification with 0xE4 type with
230 * use VPD 0x83 WWPN matching ..
231 */
232 switch (desc[0]) {
233 case 0xe4:
234 rc = target_xcopy_parse_tiddesc_e4(se_cmd, xop,
235 &desc[0], src);
236 if (rc != 0)
237 goto out;
238 /*
239 * Assume target descriptors are in source -> destination order..
240 */
241 if (src == true)
242 src = false;
243 else
244 src = true;
245 start += XCOPY_TARGET_DESC_LEN;
246 desc += XCOPY_TARGET_DESC_LEN;
247 ret++;
248 break;
249 default:
250 pr_err("XCOPY unsupported descriptor type code:"
251 " 0x%02x\n", desc[0]);
252 goto out;
253 }
254 }
255
256 if (xop->op_origin == XCOL_SOURCE_RECV_OP)
257 rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, true);
258 else
259 rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, false);
260
261 if (rc < 0)
262 goto out;
263
264 pr_debug("XCOPY TGT desc: Source dev: %p NAA IEEE WWN: 0x%16phN\n",
265 xop->src_dev, &xop->src_tid_wwn[0]);
266 pr_debug("XCOPY TGT desc: Dest dev: %p NAA IEEE WWN: 0x%16phN\n",
267 xop->dst_dev, &xop->dst_tid_wwn[0]);
268
269 return ret;
270
271out:
272 return -EINVAL;
273}
274
275static int target_xcopy_parse_segdesc_02(struct se_cmd *se_cmd, struct xcopy_op *xop,
276 unsigned char *p)
277{
278 unsigned char *desc = p;
279 int dc = (desc[1] & 0x02);
280 unsigned short desc_len;
281
282 desc_len = get_unaligned_be16(&desc[2]);
283 if (desc_len != 0x18) {
284 pr_err("XCOPY segment desc 0x02: Illegal desc_len:"
285 " %hu\n", desc_len);
286 return -EINVAL;
287 }
288
289 xop->stdi = get_unaligned_be16(&desc[4]);
290 xop->dtdi = get_unaligned_be16(&desc[6]);
291 pr_debug("XCOPY seg desc 0x02: desc_len: %hu stdi: %hu dtdi: %hu, DC: %d\n",
292 desc_len, xop->stdi, xop->dtdi, dc);
293
294 xop->nolb = get_unaligned_be16(&desc[10]);
295 xop->src_lba = get_unaligned_be64(&desc[12]);
296 xop->dst_lba = get_unaligned_be64(&desc[20]);
297 pr_debug("XCOPY seg desc 0x02: nolb: %hu src_lba: %llu dst_lba: %llu\n",
298 xop->nolb, (unsigned long long)xop->src_lba,
299 (unsigned long long)xop->dst_lba);
300
301 if (dc != 0) {
302 xop->dbl = (desc[29] << 16) & 0xff;
303 xop->dbl |= (desc[30] << 8) & 0xff;
304 xop->dbl |= desc[31] & 0xff;
305
306 pr_debug("XCOPY seg desc 0x02: DC=1 w/ dbl: %u\n", xop->dbl);
307 }
308 return 0;
309}
310
311static int target_xcopy_parse_segment_descriptors(struct se_cmd *se_cmd,
312 struct xcopy_op *xop, unsigned char *p,
313 unsigned int sdll)
314{
315 unsigned char *desc = p;
316 unsigned int start = 0;
317 int offset = sdll % XCOPY_SEGMENT_DESC_LEN, rc, ret = 0;
318
319 if (offset != 0) {
320 pr_err("XCOPY segment descriptor list length is not"
321 " multiple of %d\n", XCOPY_SEGMENT_DESC_LEN);
322 return -EINVAL;
323 }
324
325 while (start < sdll) {
326 /*
327 * Check segment descriptor type code for block -> block
328 */
329 switch (desc[0]) {
330 case 0x02:
331 rc = target_xcopy_parse_segdesc_02(se_cmd, xop, desc);
332 if (rc < 0)
333 goto out;
334
335 ret++;
336 start += XCOPY_SEGMENT_DESC_LEN;
337 desc += XCOPY_SEGMENT_DESC_LEN;
338 break;
339 default:
340 pr_err("XCOPY unspported segment descriptor"
341 "type: 0x%02x\n", desc[0]);
342 goto out;
343 }
344 }
345
346 return ret;
347
348out:
349 return -EINVAL;
350}
351
352/*
353 * Start xcopy_pt ops
354 */
355
356struct xcopy_pt_cmd {
357 bool remote_port;
358 struct se_cmd se_cmd;
359 struct xcopy_op *xcopy_op;
360 struct completion xpt_passthrough_sem;
361};
362
363static struct se_port xcopy_pt_port;
364static struct se_portal_group xcopy_pt_tpg;
365static struct se_session xcopy_pt_sess;
366static struct se_node_acl xcopy_pt_nacl;
367
368static char *xcopy_pt_get_fabric_name(void)
369{
370 return "xcopy-pt";
371}
372
373static u32 xcopy_pt_get_tag(struct se_cmd *se_cmd)
374{
375 return 0;
376}
377
378static int xcopy_pt_get_cmd_state(struct se_cmd *se_cmd)
379{
380 return 0;
381}
382
383static void xcopy_pt_undepend_remotedev(struct xcopy_op *xop)
384{
385 struct configfs_subsystem *subsys = target_core_subsystem[0];
386 struct se_device *remote_dev;
387
388 if (xop->op_origin == XCOL_SOURCE_RECV_OP)
389 remote_dev = xop->dst_dev;
390 else
391 remote_dev = xop->src_dev;
392
393 pr_debug("Calling configfs_undepend_item for subsys: %p"
394 " remote_dev: %p remote_dev->dev_group: %p\n",
395 subsys, remote_dev, &remote_dev->dev_group.cg_item);
396
397 configfs_undepend_item(subsys, &remote_dev->dev_group.cg_item);
398}
399
400static void xcopy_pt_release_cmd(struct se_cmd *se_cmd)
401{
402 struct xcopy_pt_cmd *xpt_cmd = container_of(se_cmd,
403 struct xcopy_pt_cmd, se_cmd);
404
405 if (xpt_cmd->remote_port)
406 kfree(se_cmd->se_lun);
407
408 kfree(xpt_cmd);
409}
410
411static int xcopy_pt_check_stop_free(struct se_cmd *se_cmd)
412{
413 struct xcopy_pt_cmd *xpt_cmd = container_of(se_cmd,
414 struct xcopy_pt_cmd, se_cmd);
415
416 complete(&xpt_cmd->xpt_passthrough_sem);
417 return 0;
418}
419
420static int xcopy_pt_write_pending(struct se_cmd *se_cmd)
421{
422 return 0;
423}
424
425static int xcopy_pt_write_pending_status(struct se_cmd *se_cmd)
426{
427 return 0;
428}
429
430static int xcopy_pt_queue_data_in(struct se_cmd *se_cmd)
431{
432 return 0;
433}
434
435static int xcopy_pt_queue_status(struct se_cmd *se_cmd)
436{
437 return 0;
438}
439
440static struct target_core_fabric_ops xcopy_pt_tfo = {
441 .get_fabric_name = xcopy_pt_get_fabric_name,
442 .get_task_tag = xcopy_pt_get_tag,
443 .get_cmd_state = xcopy_pt_get_cmd_state,
444 .release_cmd = xcopy_pt_release_cmd,
445 .check_stop_free = xcopy_pt_check_stop_free,
446 .write_pending = xcopy_pt_write_pending,
447 .write_pending_status = xcopy_pt_write_pending_status,
448 .queue_data_in = xcopy_pt_queue_data_in,
449 .queue_status = xcopy_pt_queue_status,
450};
451
452/*
453 * End xcopy_pt_ops
454 */
455
456int target_xcopy_setup_pt(void)
457{
458 xcopy_wq = alloc_workqueue("xcopy_wq", WQ_MEM_RECLAIM, 0);
459 if (!xcopy_wq) {
460 pr_err("Unable to allocate xcopy_wq\n");
461 return -ENOMEM;
462 }
463
464 memset(&xcopy_pt_port, 0, sizeof(struct se_port));
465 INIT_LIST_HEAD(&xcopy_pt_port.sep_alua_list);
466 INIT_LIST_HEAD(&xcopy_pt_port.sep_list);
467 mutex_init(&xcopy_pt_port.sep_tg_pt_md_mutex);
468
469 memset(&xcopy_pt_tpg, 0, sizeof(struct se_portal_group));
470 INIT_LIST_HEAD(&xcopy_pt_tpg.se_tpg_node);
471 INIT_LIST_HEAD(&xcopy_pt_tpg.acl_node_list);
472 INIT_LIST_HEAD(&xcopy_pt_tpg.tpg_sess_list);
473
474 xcopy_pt_port.sep_tpg = &xcopy_pt_tpg;
475 xcopy_pt_tpg.se_tpg_tfo = &xcopy_pt_tfo;
476
477 memset(&xcopy_pt_nacl, 0, sizeof(struct se_node_acl));
478 INIT_LIST_HEAD(&xcopy_pt_nacl.acl_list);
479 INIT_LIST_HEAD(&xcopy_pt_nacl.acl_sess_list);
480 memset(&xcopy_pt_sess, 0, sizeof(struct se_session));
481 INIT_LIST_HEAD(&xcopy_pt_sess.sess_list);
482 INIT_LIST_HEAD(&xcopy_pt_sess.sess_acl_list);
483
484 xcopy_pt_nacl.se_tpg = &xcopy_pt_tpg;
485 xcopy_pt_nacl.nacl_sess = &xcopy_pt_sess;
486
487 xcopy_pt_sess.se_tpg = &xcopy_pt_tpg;
488 xcopy_pt_sess.se_node_acl = &xcopy_pt_nacl;
489
490 return 0;
491}
492
493void target_xcopy_release_pt(void)
494{
495 if (xcopy_wq)
496 destroy_workqueue(xcopy_wq);
497}
498
499static void target_xcopy_setup_pt_port(
500 struct xcopy_pt_cmd *xpt_cmd,
501 struct xcopy_op *xop,
502 bool remote_port)
503{
504 struct se_cmd *ec_cmd = xop->xop_se_cmd;
505 struct se_cmd *pt_cmd = &xpt_cmd->se_cmd;
506
507 if (xop->op_origin == XCOL_SOURCE_RECV_OP) {
508 /*
509 * Honor destination port reservations for X-COPY PUSH emulation
510 * when CDB is received on local source port, and READs blocks to
511 * WRITE on remote destination port.
512 */
513 if (remote_port) {
514 xpt_cmd->remote_port = remote_port;
515 pt_cmd->se_lun->lun_sep = &xcopy_pt_port;
516 pr_debug("Setup emulated remote DEST xcopy_pt_port: %p to"
517 " cmd->se_lun->lun_sep for X-COPY data PUSH\n",
518 pt_cmd->se_lun->lun_sep);
519 } else {
520 pt_cmd->se_lun = ec_cmd->se_lun;
521 pt_cmd->se_dev = ec_cmd->se_dev;
522
523 pr_debug("Honoring local SRC port from ec_cmd->se_dev:"
524 " %p\n", pt_cmd->se_dev);
525 pt_cmd->se_lun = ec_cmd->se_lun;
526 pr_debug("Honoring local SRC port from ec_cmd->se_lun: %p\n",
527 pt_cmd->se_lun);
528 }
529 } else {
530 /*
531 * Honor source port reservation for X-COPY PULL emulation
532 * when CDB is received on local desintation port, and READs
533 * blocks from the remote source port to WRITE on local
534 * destination port.
535 */
536 if (remote_port) {
537 xpt_cmd->remote_port = remote_port;
538 pt_cmd->se_lun->lun_sep = &xcopy_pt_port;
539 pr_debug("Setup emulated remote SRC xcopy_pt_port: %p to"
540 " cmd->se_lun->lun_sep for X-COPY data PULL\n",
541 pt_cmd->se_lun->lun_sep);
542 } else {
543 pt_cmd->se_lun = ec_cmd->se_lun;
544 pt_cmd->se_dev = ec_cmd->se_dev;
545
546 pr_debug("Honoring local DST port from ec_cmd->se_dev:"
547 " %p\n", pt_cmd->se_dev);
548 pt_cmd->se_lun = ec_cmd->se_lun;
549 pr_debug("Honoring local DST port from ec_cmd->se_lun: %p\n",
550 pt_cmd->se_lun);
551 }
552 }
553}
554
555static int target_xcopy_init_pt_lun(
556 struct xcopy_pt_cmd *xpt_cmd,
557 struct xcopy_op *xop,
558 struct se_device *se_dev,
559 struct se_cmd *pt_cmd,
560 bool remote_port)
561{
562 /*
563 * Don't allocate + init an pt_cmd->se_lun if honoring local port for
564 * reservations. The pt_cmd->se_lun pointer will be setup from within
565 * target_xcopy_setup_pt_port()
566 */
567 if (remote_port == false) {
568 pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH;
569 return 0;
570 }
571
572 pt_cmd->se_lun = kzalloc(sizeof(struct se_lun), GFP_KERNEL);
573 if (!pt_cmd->se_lun) {
574 pr_err("Unable to allocate pt_cmd->se_lun\n");
575 return -ENOMEM;
576 }
577 init_completion(&pt_cmd->se_lun->lun_shutdown_comp);
578 INIT_LIST_HEAD(&pt_cmd->se_lun->lun_cmd_list);
579 INIT_LIST_HEAD(&pt_cmd->se_lun->lun_acl_list);
580 spin_lock_init(&pt_cmd->se_lun->lun_acl_lock);
581 spin_lock_init(&pt_cmd->se_lun->lun_cmd_lock);
582 spin_lock_init(&pt_cmd->se_lun->lun_sep_lock);
583
584 pt_cmd->se_dev = se_dev;
585
586 pr_debug("Setup emulated se_dev: %p from se_dev\n", pt_cmd->se_dev);
587 pt_cmd->se_lun->lun_se_dev = se_dev;
588 pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH;
589
590 pr_debug("Setup emulated se_dev: %p to pt_cmd->se_lun->lun_se_dev\n",
591 pt_cmd->se_lun->lun_se_dev);
592
593 return 0;
594}
595
596static int target_xcopy_setup_pt_cmd(
597 struct xcopy_pt_cmd *xpt_cmd,
598 struct xcopy_op *xop,
599 struct se_device *se_dev,
600 unsigned char *cdb,
601 bool remote_port,
602 bool alloc_mem)
603{
604 struct se_cmd *cmd = &xpt_cmd->se_cmd;
605 sense_reason_t sense_rc;
606 int ret = 0, rc;
607 /*
608 * Setup LUN+port to honor reservations based upon xop->op_origin for
609 * X-COPY PUSH or X-COPY PULL based upon where the CDB was received.
610 */
611 rc = target_xcopy_init_pt_lun(xpt_cmd, xop, se_dev, cmd, remote_port);
612 if (rc < 0) {
613 ret = rc;
614 goto out;
615 }
616 xpt_cmd->xcopy_op = xop;
617 target_xcopy_setup_pt_port(xpt_cmd, xop, remote_port);
618
619 sense_rc = target_setup_cmd_from_cdb(cmd, cdb);
620 if (sense_rc) {
621 ret = -EINVAL;
622 goto out;
623 }
624
625 if (alloc_mem) {
626 rc = target_alloc_sgl(&cmd->t_data_sg, &cmd->t_data_nents,
627 cmd->data_length, false);
628 if (rc < 0) {
629 ret = rc;
630 goto out;
631 }
632 /*
633 * Set this bit so that transport_free_pages() allows the
634 * caller to release SGLs + physical memory allocated by
635 * transport_generic_get_mem()..
636 */
637 cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
638 } else {
639 /*
640 * Here the previously allocated SGLs for the internal READ
641 * are mapped zero-copy to the internal WRITE.
642 */
643 sense_rc = transport_generic_map_mem_to_cmd(cmd,
644 xop->xop_data_sg, xop->xop_data_nents,
645 NULL, 0);
646 if (sense_rc) {
647 ret = -EINVAL;
648 goto out;
649 }
650
651 pr_debug("Setup PASSTHROUGH_NOALLOC t_data_sg: %p t_data_nents:"
652 " %u\n", cmd->t_data_sg, cmd->t_data_nents);
653 }
654
655 return 0;
656
657out:
658 if (remote_port == true)
659 kfree(cmd->se_lun);
660 return ret;
661}
662
663static int target_xcopy_issue_pt_cmd(struct xcopy_pt_cmd *xpt_cmd)
664{
665 struct se_cmd *se_cmd = &xpt_cmd->se_cmd;
666 sense_reason_t sense_rc;
667
668 sense_rc = transport_generic_new_cmd(se_cmd);
669 if (sense_rc)
670 return -EINVAL;
671
672 if (se_cmd->data_direction == DMA_TO_DEVICE)
673 target_execute_cmd(se_cmd);
674
675 wait_for_completion_interruptible(&xpt_cmd->xpt_passthrough_sem);
676
677 pr_debug("target_xcopy_issue_pt_cmd(): SCSI status: 0x%02x\n",
678 se_cmd->scsi_status);
679 return 0;
680}
681
682static int target_xcopy_read_source(
683 struct se_cmd *ec_cmd,
684 struct xcopy_op *xop,
685 struct se_device *src_dev,
686 sector_t src_lba,
687 u32 src_sectors)
688{
689 struct xcopy_pt_cmd *xpt_cmd;
690 struct se_cmd *se_cmd;
691 u32 length = (src_sectors * src_dev->dev_attrib.block_size);
692 int rc;
693 unsigned char cdb[16];
694 bool remote_port = (xop->op_origin == XCOL_DEST_RECV_OP);
695
696 xpt_cmd = kzalloc(sizeof(struct xcopy_pt_cmd), GFP_KERNEL);
697 if (!xpt_cmd) {
698 pr_err("Unable to allocate xcopy_pt_cmd\n");
699 return -ENOMEM;
700 }
701 init_completion(&xpt_cmd->xpt_passthrough_sem);
702 se_cmd = &xpt_cmd->se_cmd;
703
704 memset(&cdb[0], 0, 16);
705 cdb[0] = READ_16;
706 put_unaligned_be64(src_lba, &cdb[2]);
707 put_unaligned_be32(src_sectors, &cdb[10]);
708 pr_debug("XCOPY: Built READ_16: LBA: %llu Sectors: %u Length: %u\n",
709 (unsigned long long)src_lba, src_sectors, length);
710
711 transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
712 DMA_FROM_DEVICE, 0, NULL);
713 xop->src_pt_cmd = xpt_cmd;
714
715 rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0],
716 remote_port, true);
717 if (rc < 0) {
718 transport_generic_free_cmd(se_cmd, 0);
719 return rc;
720 }
721
722 xop->xop_data_sg = se_cmd->t_data_sg;
723 xop->xop_data_nents = se_cmd->t_data_nents;
724 pr_debug("XCOPY-READ: Saved xop->xop_data_sg: %p, num: %u for READ"
725 " memory\n", xop->xop_data_sg, xop->xop_data_nents);
726
727 rc = target_xcopy_issue_pt_cmd(xpt_cmd);
728 if (rc < 0) {
729 transport_generic_free_cmd(se_cmd, 0);
730 return rc;
731 }
732 /*
733 * Clear off the allocated t_data_sg, that has been saved for
734 * zero-copy WRITE submission reuse in struct xcopy_op..
735 */
736 se_cmd->t_data_sg = NULL;
737 se_cmd->t_data_nents = 0;
738
739 return 0;
740}
741
742static int target_xcopy_write_destination(
743 struct se_cmd *ec_cmd,
744 struct xcopy_op *xop,
745 struct se_device *dst_dev,
746 sector_t dst_lba,
747 u32 dst_sectors)
748{
749 struct xcopy_pt_cmd *xpt_cmd;
750 struct se_cmd *se_cmd;
751 u32 length = (dst_sectors * dst_dev->dev_attrib.block_size);
752 int rc;
753 unsigned char cdb[16];
754 bool remote_port = (xop->op_origin == XCOL_SOURCE_RECV_OP);
755
756 xpt_cmd = kzalloc(sizeof(struct xcopy_pt_cmd), GFP_KERNEL);
757 if (!xpt_cmd) {
758 pr_err("Unable to allocate xcopy_pt_cmd\n");
759 return -ENOMEM;
760 }
761 init_completion(&xpt_cmd->xpt_passthrough_sem);
762 se_cmd = &xpt_cmd->se_cmd;
763
764 memset(&cdb[0], 0, 16);
765 cdb[0] = WRITE_16;
766 put_unaligned_be64(dst_lba, &cdb[2]);
767 put_unaligned_be32(dst_sectors, &cdb[10]);
768 pr_debug("XCOPY: Built WRITE_16: LBA: %llu Sectors: %u Length: %u\n",
769 (unsigned long long)dst_lba, dst_sectors, length);
770
771 transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
772 DMA_TO_DEVICE, 0, NULL);
773 xop->dst_pt_cmd = xpt_cmd;
774
775 rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, dst_dev, &cdb[0],
776 remote_port, false);
777 if (rc < 0) {
778 struct se_cmd *src_cmd = &xop->src_pt_cmd->se_cmd;
779 /*
780 * If the failure happened before the t_mem_list hand-off in
781 * target_xcopy_setup_pt_cmd(), Reset memory + clear flag so that
782 * core releases this memory on error during X-COPY WRITE I/O.
783 */
784 src_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
785 src_cmd->t_data_sg = xop->xop_data_sg;
786 src_cmd->t_data_nents = xop->xop_data_nents;
787
788 transport_generic_free_cmd(se_cmd, 0);
789 return rc;
790 }
791
792 rc = target_xcopy_issue_pt_cmd(xpt_cmd);
793 if (rc < 0) {
794 se_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
795 transport_generic_free_cmd(se_cmd, 0);
796 return rc;
797 }
798
799 return 0;
800}
801
802static void target_xcopy_do_work(struct work_struct *work)
803{
804 struct xcopy_op *xop = container_of(work, struct xcopy_op, xop_work);
805 struct se_device *src_dev = xop->src_dev, *dst_dev = xop->dst_dev;
806 struct se_cmd *ec_cmd = xop->xop_se_cmd;
807 sector_t src_lba = xop->src_lba, dst_lba = xop->dst_lba, end_lba;
808 unsigned int max_sectors;
809 int rc;
810 unsigned short nolb = xop->nolb, cur_nolb, max_nolb, copied_nolb = 0;
811
812 end_lba = src_lba + nolb;
813 /*
814 * Break up XCOPY I/O into hw_max_sectors sized I/O based on the
815 * smallest max_sectors between src_dev + dev_dev, or
816 */
817 max_sectors = min(src_dev->dev_attrib.hw_max_sectors,
818 dst_dev->dev_attrib.hw_max_sectors);
819 max_sectors = min_t(u32, max_sectors, XCOPY_MAX_SECTORS);
820
821 max_nolb = min_t(u16, max_sectors, ((u16)(~0U)));
822
823 pr_debug("target_xcopy_do_work: nolb: %hu, max_nolb: %hu end_lba: %llu\n",
824 nolb, max_nolb, (unsigned long long)end_lba);
825 pr_debug("target_xcopy_do_work: Starting src_lba: %llu, dst_lba: %llu\n",
826 (unsigned long long)src_lba, (unsigned long long)dst_lba);
827
828 while (src_lba < end_lba) {
829 cur_nolb = min(nolb, max_nolb);
830
831 pr_debug("target_xcopy_do_work: Calling read src_dev: %p src_lba: %llu,"
832 " cur_nolb: %hu\n", src_dev, (unsigned long long)src_lba, cur_nolb);
833
834 rc = target_xcopy_read_source(ec_cmd, xop, src_dev, src_lba, cur_nolb);
835 if (rc < 0)
836 goto out;
837
838 src_lba += cur_nolb;
839 pr_debug("target_xcopy_do_work: Incremented READ src_lba to %llu\n",
840 (unsigned long long)src_lba);
841
842 pr_debug("target_xcopy_do_work: Calling write dst_dev: %p dst_lba: %llu,"
843 " cur_nolb: %hu\n", dst_dev, (unsigned long long)dst_lba, cur_nolb);
844
845 rc = target_xcopy_write_destination(ec_cmd, xop, dst_dev,
846 dst_lba, cur_nolb);
847 if (rc < 0) {
848 transport_generic_free_cmd(&xop->src_pt_cmd->se_cmd, 0);
849 goto out;
850 }
851
852 dst_lba += cur_nolb;
853 pr_debug("target_xcopy_do_work: Incremented WRITE dst_lba to %llu\n",
854 (unsigned long long)dst_lba);
855
856 copied_nolb += cur_nolb;
857 nolb -= cur_nolb;
858
859 transport_generic_free_cmd(&xop->src_pt_cmd->se_cmd, 0);
860 xop->dst_pt_cmd->se_cmd.se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
861
862 transport_generic_free_cmd(&xop->dst_pt_cmd->se_cmd, 0);
863 }
864
865 xcopy_pt_undepend_remotedev(xop);
866 kfree(xop);
867
868 pr_debug("target_xcopy_do_work: Final src_lba: %llu, dst_lba: %llu\n",
869 (unsigned long long)src_lba, (unsigned long long)dst_lba);
870 pr_debug("target_xcopy_do_work: Blocks copied: %hu, Bytes Copied: %u\n",
871 copied_nolb, copied_nolb * dst_dev->dev_attrib.block_size);
872
873 pr_debug("target_xcopy_do_work: Setting X-COPY GOOD status -> sending response\n");
874 target_complete_cmd(ec_cmd, SAM_STAT_GOOD);
875 return;
876
877out:
878 xcopy_pt_undepend_remotedev(xop);
879 kfree(xop);
880
881 pr_warn("target_xcopy_do_work: Setting X-COPY CHECK_CONDITION -> sending response\n");
882 ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
883 target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION);
884}
885
886sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
887{
888 struct xcopy_op *xop = NULL;
889 unsigned char *p = NULL, *seg_desc;
890 unsigned int list_id, list_id_usage, sdll, inline_dl, sa;
891 int rc;
892 unsigned short tdll;
893
894 sa = se_cmd->t_task_cdb[1] & 0x1f;
895 if (sa != 0x00) {
896 pr_err("EXTENDED_COPY(LID4) not supported\n");
897 return TCM_UNSUPPORTED_SCSI_OPCODE;
898 }
899
900 p = transport_kmap_data_sg(se_cmd);
901 if (!p) {
902 pr_err("transport_kmap_data_sg() failed in target_do_xcopy\n");
903 return TCM_OUT_OF_RESOURCES;
904 }
905
906 list_id = p[0];
907 if (list_id != 0x00) {
908 pr_err("XCOPY with non zero list_id: 0x%02x\n", list_id);
909 goto out;
910 }
911 list_id_usage = (p[1] & 0x18);
912 /*
913 * Determine TARGET DESCRIPTOR LIST LENGTH + SEGMENT DESCRIPTOR LIST LENGTH
914 */
915 tdll = get_unaligned_be16(&p[2]);
916 sdll = get_unaligned_be32(&p[8]);
917
918 inline_dl = get_unaligned_be32(&p[12]);
919 if (inline_dl != 0) {
920 pr_err("XCOPY with non zero inline data length\n");
921 goto out;
922 }
923
924 xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
925 if (!xop) {
926 pr_err("Unable to allocate xcopy_op\n");
927 goto out;
928 }
929 xop->xop_se_cmd = se_cmd;
930
931 pr_debug("Processing XCOPY with list_id: 0x%02x list_id_usage: 0x%02x"
932 " tdll: %hu sdll: %u inline_dl: %u\n", list_id, list_id_usage,
933 tdll, sdll, inline_dl);
934
935 rc = target_xcopy_parse_target_descriptors(se_cmd, xop, &p[16], tdll);
936 if (rc <= 0)
937 goto out;
938
939 pr_debug("XCOPY: Processed %d target descriptors, length: %u\n", rc,
940 rc * XCOPY_TARGET_DESC_LEN);
941 seg_desc = &p[16];
942 seg_desc += (rc * XCOPY_TARGET_DESC_LEN);
943
944 rc = target_xcopy_parse_segment_descriptors(se_cmd, xop, seg_desc, sdll);
945 if (rc <= 0) {
946 xcopy_pt_undepend_remotedev(xop);
947 goto out;
948 }
949 transport_kunmap_data_sg(se_cmd);
950
951 pr_debug("XCOPY: Processed %d segment descriptors, length: %u\n", rc,
952 rc * XCOPY_SEGMENT_DESC_LEN);
953 INIT_WORK(&xop->xop_work, target_xcopy_do_work);
954 queue_work(xcopy_wq, &xop->xop_work);
955 return TCM_NO_SENSE;
956
957out:
958 if (p)
959 transport_kunmap_data_sg(se_cmd);
960 kfree(xop);
961 return TCM_INVALID_CDB_FIELD;
962}
963
964static sense_reason_t target_rcr_operating_parameters(struct se_cmd *se_cmd)
965{
966 unsigned char *p;
967
968 p = transport_kmap_data_sg(se_cmd);
969 if (!p) {
970 pr_err("transport_kmap_data_sg failed in"
971 " target_rcr_operating_parameters\n");
972 return TCM_OUT_OF_RESOURCES;
973 }
974
975 if (se_cmd->data_length < 54) {
976 pr_err("Receive Copy Results Op Parameters length"
977 " too small: %u\n", se_cmd->data_length);
978 transport_kunmap_data_sg(se_cmd);
979 return TCM_INVALID_CDB_FIELD;
980 }
981 /*
982 * Set SNLID=1 (Supports no List ID)
983 */
984 p[4] = 0x1;
985 /*
986 * MAXIMUM TARGET DESCRIPTOR COUNT
987 */
988 put_unaligned_be16(RCR_OP_MAX_TARGET_DESC_COUNT, &p[8]);
989 /*
990 * MAXIMUM SEGMENT DESCRIPTOR COUNT
991 */
992 put_unaligned_be16(RCR_OP_MAX_SG_DESC_COUNT, &p[10]);
993 /*
994 * MAXIMUM DESCRIPTOR LIST LENGTH
995 */
996 put_unaligned_be32(RCR_OP_MAX_DESC_LIST_LEN, &p[12]);
997 /*
998 * MAXIMUM SEGMENT LENGTH
999 */
1000 put_unaligned_be32(RCR_OP_MAX_SEGMENT_LEN, &p[16]);
1001 /*
1002 * MAXIMUM INLINE DATA LENGTH for SA 0x04 (NOT SUPPORTED)
1003 */
1004 put_unaligned_be32(0x0, &p[20]);
1005 /*
1006 * HELD DATA LIMIT
1007 */
1008 put_unaligned_be32(0x0, &p[24]);
1009 /*
1010 * MAXIMUM STREAM DEVICE TRANSFER SIZE
1011 */
1012 put_unaligned_be32(0x0, &p[28]);
1013 /*
1014 * TOTAL CONCURRENT COPIES
1015 */
1016 put_unaligned_be16(RCR_OP_TOTAL_CONCURR_COPIES, &p[34]);
1017 /*
1018 * MAXIMUM CONCURRENT COPIES
1019 */
1020 p[36] = RCR_OP_MAX_CONCURR_COPIES;
1021 /*
1022 * DATA SEGMENT GRANULARITY (log 2)
1023 */
1024 p[37] = RCR_OP_DATA_SEG_GRAN_LOG2;
1025 /*
1026 * INLINE DATA GRANULARITY log 2)
1027 */
1028 p[38] = RCR_OP_INLINE_DATA_GRAN_LOG2;
1029 /*
1030 * HELD DATA GRANULARITY
1031 */
1032 p[39] = RCR_OP_HELD_DATA_GRAN_LOG2;
1033 /*
1034 * IMPLEMENTED DESCRIPTOR LIST LENGTH
1035 */
1036 p[43] = 0x2;
1037 /*
1038 * List of implemented descriptor type codes (ordered)
1039 */
1040 p[44] = 0x02; /* Copy Block to Block device */
1041 p[45] = 0xe4; /* Identification descriptor target descriptor */
1042
1043 /*
1044 * AVAILABLE DATA (n-3)
1045 */
1046 put_unaligned_be32(42, &p[0]);
1047
1048 transport_kunmap_data_sg(se_cmd);
1049 target_complete_cmd(se_cmd, GOOD);
1050
1051 return TCM_NO_SENSE;
1052}
1053
1054sense_reason_t target_do_receive_copy_results(struct se_cmd *se_cmd)
1055{
1056 unsigned char *cdb = &se_cmd->t_task_cdb[0];
1057 int sa = (cdb[1] & 0x1f), list_id = cdb[2];
1058 sense_reason_t rc = TCM_NO_SENSE;
1059
1060 pr_debug("Entering target_do_receive_copy_results: SA: 0x%02x, List ID:"
1061 " 0x%02x, AL: %u\n", sa, list_id, se_cmd->data_length);
1062
1063 if (list_id != 0) {
1064 pr_err("Receive Copy Results with non zero list identifier"
1065 " not supported\n");
1066 return TCM_INVALID_CDB_FIELD;
1067 }
1068
1069 switch (sa) {
1070 case RCR_SA_OPERATING_PARAMETERS:
1071 rc = target_rcr_operating_parameters(se_cmd);
1072 break;
1073 case RCR_SA_COPY_STATUS:
1074 case RCR_SA_RECEIVE_DATA:
1075 case RCR_SA_FAILED_SEGMENT_DETAILS:
1076 default:
1077 pr_err("Unsupported SA for receive copy results: 0x%02x\n", sa);
1078 return TCM_INVALID_CDB_FIELD;
1079 }
1080
1081 return rc;
1082}
diff --git a/drivers/target/target_core_xcopy.h b/drivers/target/target_core_xcopy.h
new file mode 100644
index 000000000000..700a981c7b41
--- /dev/null
+++ b/drivers/target/target_core_xcopy.h
@@ -0,0 +1,62 @@
1#define XCOPY_TARGET_DESC_LEN 32
2#define XCOPY_SEGMENT_DESC_LEN 28
3#define XCOPY_NAA_IEEE_REGEX_LEN 16
4#define XCOPY_MAX_SECTORS 1024
5
6enum xcopy_origin_list {
7 XCOL_SOURCE_RECV_OP = 0x01,
8 XCOL_DEST_RECV_OP = 0x02,
9};
10
11struct xcopy_pt_cmd;
12
13struct xcopy_op {
14 int op_origin;
15
16 struct se_cmd *xop_se_cmd;
17 struct se_device *src_dev;
18 unsigned char src_tid_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
19 struct se_device *dst_dev;
20 unsigned char dst_tid_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
21 unsigned char local_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
22
23 sector_t src_lba;
24 sector_t dst_lba;
25 unsigned short stdi;
26 unsigned short dtdi;
27 unsigned short nolb;
28 unsigned int dbl;
29
30 struct xcopy_pt_cmd *src_pt_cmd;
31 struct xcopy_pt_cmd *dst_pt_cmd;
32
33 u32 xop_data_nents;
34 struct scatterlist *xop_data_sg;
35 struct work_struct xop_work;
36};
37
38/*
39 * Receive Copy Results Sevice Actions
40 */
41#define RCR_SA_COPY_STATUS 0x00
42#define RCR_SA_RECEIVE_DATA 0x01
43#define RCR_SA_OPERATING_PARAMETERS 0x03
44#define RCR_SA_FAILED_SEGMENT_DETAILS 0x04
45
46/*
47 * Receive Copy Results defs for Operating Parameters
48 */
49#define RCR_OP_MAX_TARGET_DESC_COUNT 0x2
50#define RCR_OP_MAX_SG_DESC_COUNT 0x1
51#define RCR_OP_MAX_DESC_LIST_LEN 1024
52#define RCR_OP_MAX_SEGMENT_LEN 268435456 /* 256 MB */
53#define RCR_OP_TOTAL_CONCURR_COPIES 0x1 /* Must be <= 16384 */
54#define RCR_OP_MAX_CONCURR_COPIES 0x1 /* Must be <= 255 */
55#define RCR_OP_DATA_SEG_GRAN_LOG2 9 /* 512 bytes in log 2 */
56#define RCR_OP_INLINE_DATA_GRAN_LOG2 9 /* 512 bytes in log 2 */
57#define RCR_OP_HELD_DATA_GRAN_LOG2 9 /* 512 bytes in log 2 */
58
59extern int target_xcopy_setup_pt(void);
60extern void target_xcopy_release_pt(void);
61extern sense_reason_t target_do_xcopy(struct se_cmd *);
62extern sense_reason_t target_do_receive_copy_results(struct se_cmd *);