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 { |