[Stgt-devel] tape drive not supported yet?

FUJITA Tomonori fujita.tomonori
Wed Jul 16 09:51:38 CEST 2008


On Sat, 12 Jul 2008 19:40:21 +0200
Albert Pauw <albert.pauw at gmail.com> wrote:

> Tried to set up a tape drive target:
> 
> LUN=$(($LUN+1))
> ID=Tape
> SN=54321
> tgtadm --lld iscsi --op new --mode logicalunit --tid $TID --lun $LUN \
> --device-type tape
> 
> Unfortunately, tgtadm tells me:
> 
> tgtadm: type emulation isn't supported yet
> 
> Is this correct?

You are talking about vtl, right?

Here's aftab azmi's vtl patch:

https://lists.berlios.de/pipermail/stgt-devel/2008-May/001610.html

I've cleaned up it and my initiator can find a tape drive with this
pathc. However, seems that this patch has several issues to solve.

Mark's comments are:

https://lists.berlios.de/pipermail/stgt-devel/2008-May/001622.html

I need vtl to just work on the tape initiator driver. I'll try to
improve the vtl support but probabaly I don't have time to make the
vtl support good enough for real usage.


BTW, here's an example how to setup a tape drive with this patch:

tgtadm --lld iscsi --op new --mode target --tid 1 -T iqn.2001-04.org.osrg:viola
tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 -b /var/tmp/image0 --device-type ssc --bstype ssc

=
diff --git a/usr/Makefile b/usr/Makefile
index 48de052..4aae4c5 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -DISCSI
 TGTD_OBJS += $(addprefix iscsi/, conn.o param.o session.o \
 		iscsid.o target.o chap.o transport.o iscsi_tcp.o \
 		isns.o libcrc32c.o)
-TGTD_OBJS += bs_rdwr.o bs_aio.o
+TGTD_OBJS += bs_rdwr.o bs_aio.o bs_ssc.o
 
 LIBS += -lcrypto
 ifneq ($(ISCSI_RDMA),)
@@ -57,7 +57,7 @@ LIBS += -lpthread
 PROGRAMS += tgtd tgtadm
 SCRIPTS += ../scripts/tgt-setup-lun
 TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o \
-		parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o bs.o
+		parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs.o
 MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-setup-lun.8
 
 TGTD_DEP = $(TGTD_OBJS:.o=.d)
diff --git a/usr/bs_ssc.c b/usr/bs_ssc.c
new file mode 100644
index 0000000..78d5817
--- /dev/null
+++ b/usr/bs_ssc.c
@@ -0,0 +1,250 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/fs.h>
+#include <sys/epoll.h>
+
+#include "list.h"
+#include "util.h"
+#include "tgtd.h"
+#include "scsi.h"
+#include "bs_thread.h"
+
+#define TCLP_BIT            4
+#define LONG_BIT            2
+#define BT_BIT              1
+
+static void set_medium_error(int *result, uint8_t *key, uint16_t *asc)
+{
+	*result = SAM_STAT_CHECK_CONDITION;
+	*key = MEDIUM_ERROR;
+	*asc = ASC_READ_ERROR;
+}
+
+static void ssc_sense_data_build(struct scsi_cmd *cmd, uint8_t key,
+				 uint16_t asc)
+{
+	int len = 0xa;
+	cmd->sense_buffer[0] = 0x70;
+	cmd->sense_buffer[2] = NO_SENSE;
+	cmd->sense_buffer[7] = len;
+	cmd->sense_buffer[12] = (asc >> 8) & 0xff;
+	cmd->sense_buffer[13] = asc & 0xff;
+	cmd->sense_len = len + 8;
+}
+
+static void rdwr_request(struct scsi_cmd *cmd)
+{
+	int ret, fd = cmd->dev->fd, code;
+	uint32_t length, i, transfer_length, residue;
+	int result = SAM_STAT_GOOD;
+	uint8_t key;
+	uint16_t asc;
+	uint8_t buff[512];
+	char *buf;
+	off_t rew;
+	uint64_t curr_pos;
+	uint32_t count;
+
+	ret = 0;
+	length = 0;
+	i = 0;
+	key = 0;
+	asc = 0;
+	transfer_length = 0;
+	residue = 0;
+	count = 0;
+	code = 0;
+
+	switch (cmd->scb[0]) {
+	case REZERO_UNIT:
+		rew = lseek(fd, 0, SEEK_SET);
+		curr_pos = lseek(fd, 0, SEEK_CUR);
+		if (ret)
+			set_medium_error(&result, &key, &asc);
+		eprintf("Rewind Successful, File Pointer at %" PRIu64",%m\n",
+			curr_pos);
+		break;
+	case WRITE_FILEMARKS:
+		length = sizeof(buff);
+		memset(buff, 28, sizeof(buff));
+		ret = write(fd, buff, length);
+
+		if (ret != length)
+			set_medium_error(&result, &key, &asc);
+		eprintf("Write Filemark Successfull %d\n", ret);
+		curr_pos = lseek(fd, 0, SEEK_CUR);
+		eprintf("File Pointer at %" PRIu64",%m\n", curr_pos);
+		break;
+	case READ_6:
+		length = scsi_get_in_length(cmd);
+		ret = read(fd, scsi_get_in_buffer(cmd), length);
+		buf = (char *)(unsigned long)scsi_get_in_buffer(cmd);
+		/* buf = (char *)buf; */
+		if (ret != length)
+			set_medium_error(&result, &key, &asc);
+		else {
+			for (i = 0; i < ret; i += 512) {
+				eprintf("buf[%d]=%d", i, buf[i]);
+				if (buf[i] == 28) {
+					result = SAM_STAT_CHECK_CONDITION;
+					key = NO_SENSE;
+					asc = ASC_MARK;
+					transfer_length = ((cmd->scb[2] << 16) |
+							   (cmd->scb[3] << 8) |
+							   (cmd->scb[4]));
+/* 					residue = */
+/* 						transfer_length - i << 9; */
+					residue = (length - i) << 9;
+					cmd->sense_buffer[3] = residue >> 24;
+					cmd->sense_buffer[3] = residue >> 16;
+					cmd->sense_buffer[3] = residue >> 8;
+					cmd->sense_buffer[3] = residue;
+
+					eprintf("File Mark Detected at %d,"
+						" Residue = %d %m\n",
+						i, residue);
+				}
+			}
+		}
+		eprintf("Executed READ_6, Read %d bytes\n", ret);
+		curr_pos = lseek(fd, 0, SEEK_CUR);
+		eprintf("File Pointer at %" PRIu64",%m\n", curr_pos);
+		break;
+	case WRITE_6:
+		length = scsi_get_out_length(cmd);
+		ret = write(fd, scsi_get_out_buffer(cmd), length);
+		if (ret != length)
+			set_medium_error(&result, &key, &asc);
+		eprintf("Executed WRITE_6, writen %d bytes\n", ret);
+		curr_pos = lseek(fd, 0, SEEK_CUR);
+		eprintf("File Pointer at %" PRIu64",%m\n", curr_pos);
+		break;
+	case SPACE:
+		code = cmd->scb[1];
+		count = (cmd->scb[2] << 16) | (cmd->scb[3] << 8) |
+			(cmd->scb[4]);
+
+		if (code == 0) {
+			for (i = 0; i < count; i++) {
+				ret = read(fd, buff, sizeof(buff));
+				if (ret != sizeof(buff))
+					set_medium_error(&result, &key, &asc);
+
+				curr_pos = lseek(fd, 0, SEEK_CUR);
+				eprintf("File Pointer at %" PRIu64",%m\n",
+					curr_pos);
+
+				if (buff[i*512] == 28) {
+					result = SAM_STAT_CHECK_CONDITION;
+					key = NO_SENSE;
+					asc = ASC_MARK;
+				}
+			}
+		} else if (code == 1) {
+			i = 0;
+			while (i < count) {
+				ret = read(fd, buff, sizeof(buff));
+				curr_pos = lseek(fd, 0, SEEK_CUR);
+				eprintf("File Pointer at %" PRIu64",%m\n",
+					curr_pos);
+				if (buff[i*512] == 28)
+					i++;
+			}
+		}
+		break;
+	case READ_POSITION:
+	{
+		int tclp = cmd->scb[1] & TCLP_BIT;
+		int long_bit = cmd->scb[1] & LONG_BIT;
+		int bt = cmd->scb[1] & BT_BIT;
+		uint8_t *data;
+
+		eprintf("Size of in_buffer = %d ", scsi_get_in_length(cmd));
+		if (tclp == 1 || tclp != long_bit || (bt == 1 && long_bit == 1))
+			result = SAM_STAT_CHECK_CONDITION;
+		else {
+			memset(buff, 0, sizeof(buff));
+			data = buff;
+			curr_pos = lseek(fd, 0, SEEK_CUR);
+			if (curr_pos == 0)
+				data[0] = 0xb4;
+			else
+				data[0] = 0x34;
+			memcpy(scsi_get_in_buffer(cmd), data, 20);
+		}
+
+		break;
+	}
+	default:
+		break;
+	}
+
+	dprintf("io done %p %x %d %u\n", cmd, cmd->scb[0], ret, length);
+
+	scsi_set_result(cmd, result);
+
+	if (result != SAM_STAT_GOOD) {
+		eprintf("io error %p %x %d %d %" PRIu64 ", %m\n",
+			cmd, cmd->scb[0], ret, length, cmd->offset);
+		ssc_sense_data_build(cmd, key, asc);
+	}
+}
+
+
+static int bs_ssc_open(struct scsi_lu *lu, char *path, int *fd, uint64_t *size)
+{
+	int ret;
+	struct bs_thread_info *info = BS_THREAD_I(lu);
+	uint64_t curr_pos;
+
+	eprintf("In bs_ssc_open\n");
+	*fd = backed_file_open(path, O_RDWR | O_LARGEFILE, size);
+	if (*fd < 0) {
+		eprintf("Error in bs_ssc_open\n");
+		return *fd;
+	}
+	curr_pos = lseek(*fd, 0, SEEK_CUR);
+	eprintf("File %s File Pointer at %" PRIu64",%m\n", path, curr_pos);
+
+	ret = bs_thread_open(info, rdwr_request);
+	if (ret) {
+		close(*fd);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void bs_ssc_close(struct scsi_lu *lu)
+{
+	struct bs_thread_info *info = BS_THREAD_I(lu);
+
+	bs_thread_close(info);
+
+	close(lu->fd);
+}
+
+static int bs_ssc_cmd_done(struct scsi_cmd *cmd)
+{
+	return 0;
+}
+
+static struct backingstore_template ssc_bst = {
+	.bs_name		= "ssc",
+	.bs_datasize		= sizeof(struct bs_thread_info),
+	.bs_open		= bs_ssc_open,
+	.bs_close		= bs_ssc_close,
+	.bs_cmd_submit		= bs_thread_cmd_submit,
+	.bs_cmd_done		= bs_ssc_cmd_done,
+};
+
+__attribute__((constructor)) static void bs_ssc_constructor(void)
+{
+	register_backingstore_template(&ssc_bst);
+}
diff --git a/usr/ssc.c b/usr/ssc.c
new file mode 100644
index 0000000..4363d4b
--- /dev/null
+++ b/usr/ssc.c
@@ -0,0 +1,300 @@
+/*
+ * SCSI stream command processing
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <linux/fs.h>
+
+#include "list.h"
+#include "util.h"
+#include "tgtd.h"
+#include "tgtadm_error.h"
+#include "target.h"
+#include "driver.h"
+#include "scsi.h"
+#include "spc.h"
+#include "tgtadm_error.h"
+
+#define BLK_SHIFT	9
+
+
+static int ssc_rw(int host_no, struct scsi_cmd *cmd)
+{
+	int ret;
+	unsigned char key = ILLEGAL_REQUEST;
+	uint16_t asc = ASC_LUN_NOT_SUPPORTED;
+
+	ret = device_reserved(cmd);
+	if (ret)
+		return SAM_STAT_RESERVATION_CONFLICT;
+
+	cmd->scsi_cmd_done = target_cmd_io_done;
+
+/* 	cmd->offset = (((cmd->scb[2] << 16) | (cmd->scb[3] << 8) | */
+/* 			(cmd->scb[4])) << BLK_SHIFT); */
+
+	ret = cmd->dev->bst->bs_cmd_submit(cmd);
+	if (ret) {
+		key = HARDWARE_ERROR;
+		asc = ASC_INTERNAL_TGT_FAILURE;
+	} else {
+		set_cmd_mmapio(cmd);
+		return SAM_STAT_GOOD;
+	}
+
+	cmd->offset = 0;
+	scsi_set_in_resid_by_actual(cmd, 0);
+	scsi_set_out_resid_by_actual(cmd, 0);
+
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int ssc_read_block_limit(int host_no, struct scsi_cmd *cmd)
+{
+	uint8_t *data;
+	uint8_t buf[256];
+	uint16_t blk_len = 0x200;
+
+	memset(buf, 0, sizeof(buf));
+	data = buf;
+
+	data[0] = 9;
+	data[2] = blk_len >> 8;
+	data[3] = blk_len & 0x0ff;
+	data[5] = blk_len >> 8;
+	data[6] = blk_len & 0x0ff;
+
+	memcpy(scsi_get_in_buffer(cmd), data, 6);
+	eprintf("In ssc_read_block_limit \n");
+	return SAM_STAT_GOOD;
+}
+
+static int ssc_lu_init(struct scsi_lu *lu)
+{
+	uint64_t size;
+	uint8_t *data;
+
+	if (spc_lu_init(lu))
+		return TGTADM_NOMEM;
+
+	strncpy(lu->attrs.product_id, "VIRTUAL-TAPE",
+		sizeof(lu->attrs.product_id));
+	lu->attrs.version_desc[0] = 0x0200; /* SSC no version claimed */
+	lu->attrs.version_desc[1] = 0x0960; /* iSCSI */
+	lu->attrs.version_desc[2] = 0x0300; /* SPC-3 */
+
+	data = lu->mode_block_descriptor;
+	size = lu->size >> BLK_SHIFT;
+
+	*(uint32_t *)(data) = (size >> 32) ?
+			__cpu_to_be32(0xffffffff) : __cpu_to_be32(size);
+	*(uint32_t *)(data + 4) = __cpu_to_be32(1 << BLK_SHIFT);
+
+	/* Vendor uniq - However most apps seem to call for mode page 0*/
+	add_mode_page(lu, "0:0:0");
+	/* Disconnect page */
+	add_mode_page(lu, "2:0:14:0x80:0x80:0:0xa:0:0:0:0:0:0:0:0:0:0");
+	/* Data Compression Page */
+	add_mode_page(lu, "15:0:12:0:0:0:0:0:0:0:0:0:0:0:0");
+	/* Device Configuration Page */
+	add_mode_page(lu, "0x10:0:11:0:0:0:0:0:0:0:0:0x48:0:0");
+	/* Control page */
+	add_mode_page(lu, "10:0:10:2:0:0:0:0:0:0:0:2:0");
+	/* Informational Exceptions Control page */
+	add_mode_page(lu, "0x1c:0:10:8:0:0:0:0:0:0:0:0:0");
+
+	return 0;
+}
+
+static struct device_type_template ssc_template = {
+	.type		= TYPE_TAPE,
+	.lu_init	= ssc_lu_init,
+	.lu_config	= spc_lu_config,
+	.lu_online	= spc_lu_online,
+	.lu_offline	= spc_lu_offline,
+	.lu_exit	= spc_lu_exit,
+
+	.ops		= {
+		{spc_test_unit,},
+		{ssc_rw,},
+		{spc_illegal_op,},
+		{spc_request_sense,},
+		{spc_illegal_op,},
+		{ssc_read_block_limit,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{ssc_rw,},
+		{spc_illegal_op,},
+		{ssc_rw,},
+		{ssc_rw,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		/* 0x10 */
+		{ssc_rw,},
+		{ssc_rw,},
+		{spc_inquiry,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_mode_sense,},
+		{spc_start_stop,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		/* 0x20 */
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		/* 0x30 */
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{ssc_rw,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		[0x40 ... 0x4f] = {spc_illegal_op,},
+
+		/* 0x50 */
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_mode_sense,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		[0x60 ... 0x7f] = {spc_illegal_op,},
+
+		/* 0x80 */
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		/* 0x90 */
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		/* 0xA0 */
+		{spc_report_luns,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+		{spc_illegal_op,},
+
+		[0xb0 ... 0xff] = {spc_illegal_op},
+	}
+};
+
+__attribute__((constructor)) static void ssc_init(void)
+{
+	device_type_register(&ssc_template);
+}
diff --git a/usr/tgtadm.c b/usr/tgtadm.c
index ddceb7f..9e90b40 100644
--- a/usr/tgtadm.c
+++ b/usr/tgtadm.c
@@ -306,6 +306,8 @@ static int str_to_device_type(char *str)
 		return TYPE_MEDIUM_CHANGER;
 	else if (!strcmp(str, "osd"))
 		return TYPE_OSD;
+	else if (!strcmp(str, "ssc"))
+		return TYPE_TAPE;
 	else if (!strcmp(str, "pt"))
 		return TYPE_SPT;
 	else {



More information about the stgt mailing list