imageПосле просмотра серии видеороликов «Данные на магнитной ленте» от Макс “Fagear” Крюков. Захотелось себе приобрести, что-нибудь из стримеров. Требование было чтобы оно ещё работало, стоило не как крыло самолета. И было вполне годно не только чтобы проиграться. Порывшись на барахолках, был найден стример HP LTO-1, со SCSI контроллером и кучей картриджей небольшой юзаности. Подключил к компу, погонял стандартной XPшной прогой для бэкапов. Всё работает всё хорошо. Но выяснилась одна проблема, слоты PCI на материнке расположены так, что две PCI карты туда нормально не воткнуть. А убирать X-Fi не хотелось, к тому же SCSI контроллер греется, и желательно бы его охлаждать, а это лишний шум который не нужен. Было принято решение поставить стример в другой комп(благо ненужных комплектующих хватает). Поставить Linux, и пробросить стример по iSCSI. А уже включать по мере необходимости, или вообще держать там, где шума не слышно. Опыт работы с iSCSI был, всё должно было заработать без сучка и без задоринки.

Попытка первая SCST


Уже около семи лет я использую SCST для проброски жестких дисков или их образов по iSCSI. Проблем никогда не было. Более того, там есть модуль, заточенный под проброску стримеров. Настраиваю конфиг, запускаю. Подключаюсь по iSCSI, устройство находится, жизнь прекрасна. Но попытка записать, что-либо на картридж вызывает ошибку.

Попытка заглянуть в исходники, успехом не увенчалось. Как работает SCSI, я примерно знаю. Но как оно работает в ядре Linux это для меня загадка. И пока разбираться необходимости не было. В целом я отыскал, где пересылаются команды. Но там было много не очень понятных мест. Ну и к тому же SCST явно делал не всегда то, что его просили. Например если команда завершалась ошибкой мог по своему усмотрению послать доп.команды для уточнения ошибки. А разбираться в таком без крайней необходимости, то ещё удовольствие.
В итоге решил поискать другое решение, попроще.

Попытка вторая TGT(STGT)


На сайте же SCST было найдено сравнение других вариантов iSCSI target. Поддержка SCSI pass-through была у SCST, STGT и LIO/TCM. SCST отпал из за неработоспособности(ну или кривых рук). Про LIO/TCM я мало что слышал, с STGT вроде бы, когда-то работал. Ну, плюс ко всему LIO/TCM был kernel-mode, что в случае дебага сразу бы усложнило задачу. А вот STGT полностью UserSpace, что обещает ниже производительность, но и меньше проблем в случае дебага.

Почитал документацию проброска устройства выполняется через модуль bs_sg, который работает с SG(SCSI Generic) устройствами. То есть по факту ему всё равно, с чем работать, что внушало надежды. Скомпилировалось и запустилось всё без проблем. Настроил проброс SG устройства.

./tgtadm --lld iscsi --op new --mode target --tid 1 -T iqn.2022-03.home.storage:storage.streamers.org
./tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 --bstype=sg --device-type=pt -b /dev/sg1
./tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL

Подключился инициатором, система увидела стример, попытка записать в NTBackup завершилась успешно. Казалось бы победа!

Но как гласит одно из правил бэкапов, тестируйте резервные копии. Пытаемся прочитать записанное на ленту. И получаем облом, система говорит, мол, не тот картридж воткнут, и выплевывает его.


Подключил стример напрямую к компу, данные восстанавливаются нормально, контрольные суммы сходятся. Вернул на iSCSI говорит не тот картридж вставлен.

Дебаг так дебаг


Посмотрев код bs_sg.c был приятно удивлен, всё написано просто и понятно. Главное внимательно читать и не путать функции bsg и sg. Судя по коду было две интересные функции:

static int bs_sg_cmd_submit(struct scsi_cmd *cmd)
static void bs_sg_cmd_complete(int fd, int events, void *data)

Первая принимает команду и пересылает её SG устройству, вторая вызывается, когда команда обработана и отсылает результат запросившему. Теперь надо было понять, что не так и как это решить. Для этого надо разобраться, как из винды слать SCSI команды, и как тоже самое делать из линукса чтобы можно было сравнить вызовы и результаты. В винде за это отвечает SPTI(по крайней мере, в XP дальше пока было неинтересно). Как с ним работать можно почитать в статье RSDN Укрощение строптивого… CD-ROM. Как работать с SG устройствами частично описано здесь.

Пришло время заняться отладкой, дебагер вещь хорошая, но тут хотелось обойтись банальными printf. Хотя бы на первом этапе. Функция bs_sg_cmd_complete была изменена так, чтобы выводить на экран, что посылали SG устройству, и что оно вернуло.

Код получившейся функции
static void bs_sg_cmd_complete(int fd, int events, void *data)
{
    struct sg_io_hdr io_hdr;
    struct scsi_cmd *cmd;
    int err;
    uint32_t actual_len;

    memset(&io_hdr, 0, sizeof(io_hdr));
    io_hdr.interface_id = 'S';
    io_hdr.pack_id = -1;

    err = graceful_read(fd, &io_hdr, sizeof(io_hdr));
    if (err)
        return;

    cmd = (struct scsi_cmd *)io_hdr.usr_ptr;

    printf("CDB:");
    for(uint8_t i=0; i < cmd->scb_len; i++){
        printf("%02X ", cmd->scb[i]);
    }

    printf("STATUS: %02X, LEN: %02X ", io_hdr.status, io_hdr.dxfer_len - io_hdr.resid);

    printf("SENS[%d]:", io_hdr.sb_len_wr);
    for(uint8_t i=0; i<io_hdr.sb_len_wr; i++){
        printf("%02X ", io_hdr.sbp[i]);
    }
    printf("\r\n");

    if (!io_hdr.status) {
        actual_len = io_hdr.dxfer_len - io_hdr.resid;
    } else {
        /* NO SENSE | ILI (Incorrect Length Indicator) */
        if (io_hdr.sbp[2] == 0x20)
            actual_len = io_hdr.dxfer_len - io_hdr.resid;
        else
            actual_len = 0;

        cmd->sense_len = io_hdr.sb_len_wr;

    }


    if (!actual_len || io_hdr.resid) {
        if (io_hdr.dxfer_direction == SG_DXFER_TO_DEV)
            scsi_set_out_resid_by_actual(cmd, actual_len);
        else
            scsi_set_in_resid_by_actual(cmd, actual_len);
    }

    target_cmd_io_done(cmd, io_hdr.status);
}

  


Запускаем пробуем прочитать данные с ленты и смотрим на полученный лог(удалены команды check condition(0x00)):

Лог последовательности команд
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:1B 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:1E 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:1A 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:1A 00 10 00 1C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 1C SENS[0]:
CDB:1A 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:1A 08 10 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:1A 08 0F 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 06 SENS[0]:
CDB:1A 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:15 10 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:1A 08 10 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:1A 08 0F 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 06 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:1A 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:1A 08 10 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:1A 08 0F 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 06 SENS[0]:
CDB:1A 08 10 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:1A 08 0F 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 06 SENS[0]:
CDB:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:1A 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:08 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 02, LEN: 400 SENS[22]:F0 00 80 00 00 01 FE 0E 00 00 00 00 00 01 00 00 30 21 00 00 00 00
CDB:1A 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:1A 08 10 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:1A 08 0F 00 14 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 14 SENS[0]:
CDB:05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 06 SENS[0]:
CDB:1A 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:15 10 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 0C SENS[0]:
CDB:01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:4D 00 31 00 00 00 00 00 3C 00 00 00 00 00 00 00 STATUS: 00, LEN: 24 SENS[0]:
CDB:1A 00 10 00 1C 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 1C SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:03 00 00 00 16 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 16 SENS[0]:
CDB:1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:
CDB:1B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 00, LEN: 00 SENS[0]:


Первый байт CDB это номер SCSI команды, со списком можно ознакомиться здесь. Статус показывает, успешность окончания команды:

00h GOOD
02h CHECK CONDITION
04h CONDITION MET
08h BUSY
18h RESERVATION CONFLICT
28h TASK SET FULL
30h ACA ACTIVE
40h TASK ABORTED

SENS соответственно дополнительная информация о ошибке с которой завершилась команда.
Почти все команды носят информационный характер, чтения ошибок, чтение объема картриджа, итд.

И все выполняются без ошибок кроме команды 08 (READ(6)). Попробовал посмотреть, что возвращается при выполнении этой команды из винды. Плюс это единственная команда, которая что-то читает с реальной ленты. Набросал простую программку:

Код программы для теста
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winioctl.h>
#include <ntddscsi.h>

#define SPT_CDB_LENGTH 32
#define SPT_SENSE_LENGTH 32
#define SPTWB_DATA_LENGTH 32768

#define offsetof(st, m) \
((size_t)&(((st *)0)->m))

#pragma pack(push, mode_params, 1)
typedef struct _MODE_PARAMETER_HEADER {
UCHAR ModeDataLength;
UCHAR MediumType;
UCHAR DeviceSpecificParameter;
UCHAR BlockDescriptorLength;
}MODE_PARAMETER_HEADER, *PMODE_PARAMETER_HEADER;
#pragma pack(pop, mode_params)

#pragma pack(push, mode_params_block, 1)
typedef struct _MODE_PARAMETER_BLOCK {
UCHAR DensityCode;
UCHAR NumberOfBlocks[3];
UCHAR Reserved;
UCHAR BlockLength[3];
}MODE_PARAMETER_BLOCK, *PMODE_PARAMETER_BLOCK;
#pragma pack(pop, mode_params_block)

typedef struct _MODE_DEVICE_CONFIGURATION_PAGE {

UCHAR PageCode : 6;
UCHAR Reserved1 : 1;
UCHAR PS : 1;
UCHAR PageLength;
UCHAR ActiveFormat : 5;
UCHAR CAFBit : 1;
UCHAR CAPBit : 1;
UCHAR Reserved2 : 1;
UCHAR ActivePartition;
UCHAR WriteBufferFullRatio;
UCHAR ReadBufferEmptyRatio;
UCHAR WriteDelayTime[2];
UCHAR REW : 1;
UCHAR RBO : 1;
UCHAR SOCF : 2;
UCHAR AVC : 1;
UCHAR RSmk : 1;
UCHAR BIS : 1;
UCHAR DBR : 1;
UCHAR GapSize;
UCHAR Reserved3 : 3;
UCHAR SEW : 1;
UCHAR EEG : 1;
UCHAR EODdefined : 3;
UCHAR BufferSize[3];
UCHAR DCAlgorithm;
UCHAR Reserved4;

} MODE_DEVICE_CONFIGURATION_PAGE, *PMODE_DEVICE_CONFIGURATION_PAGE;

typedef struct _MODE_DEVICE_CONFIG_PAGE_PLUS {

MODE_PARAMETER_HEADER ParameterListHeader;
MODE_PARAMETER_BLOCK ParameterListBlock;
MODE_DEVICE_CONFIGURATION_PAGE DeviceConfigPage;

} MODE_DEVICE_CONFIG_PAGE_PLUS, *PMODE_DEVICE_CONFIG_PAGE_PLUS ;

typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS {
SCSI_PASS_THROUGH spt;
ULONG Filler; // realign buffers to double word boundary
UCHAR ucSenseBuf[SPT_SENSE_LENGTH];
UCHAR ucDataBuf[SPTWB_DATA_LENGTH];
} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS;

SCSI_PASS_THROUGH_WITH_BUFFERS spti;
SCSI_INQUIRY_DATA inq;
int main(){
ULONG length = 0,
errorCode = 0,
returned = 0,
sectorSize = 512;
BOOL status = 0;
int i;
HANDLE x;
PMODE_DEVICE_CONFIG_PAGE_PLUS configInformation;

FILE * file;
x = CreateFileA("\\\\.\\tape0",
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0 , NULL);

ZeroMemory(&spti,sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS));
spti.spt.Length = sizeof(SCSI_PASS_THROUGH);
spti.spt.PathId = 0;
spti.spt.TargetId = 1;
spti.spt.Lun = 0;
spti.spt.CdbLength = 6;
spti.spt.SenseInfoLength = SPT_SENSE_LENGTH;
spti.spt.DataIn = SCSI_IOCTL_DATA_IN;
spti.spt.DataTransferLength = 0x200;//sizeof(spti.ucDataBuf);
spti.spt.TimeOutValue = 2;
spti.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf);
spti.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucSenseBuf);
spti.spt.Cdb[0] = 0x08;
spti.spt.Cdb[1] = 0x01;
spti.spt.Cdb[2] = 0x00;
spti.spt.Cdb[3] = 0x02;
spti.spt.Cdb[4] = 0x00;
spti.spt.Cdb[5] = 0x00;

length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf) + spti.spt.DataTransferLength;
memset(spti.ucSenseBuf, 0xFF, sizeof(spti.ucSenseBuf));\
status = DeviceIoControl(x, IOCTL_SCSI_PASS_THROUGH,
&spti,
sizeof(SCSI_PASS_THROUGH),
&spti,
length,
&returned,
FALSE);

printf("STATUS: %d[%02X]\r\n", status, spti.spt.ScsiStatus);

printf("DATA[%d]:", spti.spt.DataTransferLength);
for(i=0; i<spti.spt.DataTransferLength && i < 15; i++){
printf("%02X ", spti.ucDataBuf[i]);
}
printf("\r\n");

printf("SENS[%d]:", spti.spt.SenseInfoLength);
for(i=0; i<spti.spt.SenseInfoLength; i++){
printf("%02X ", spti.ucSenseBuf[i]);
}
printf("\r\n");

return 0;
}


Запускаю и что мы видим:

STATUS: 1[02]
DATA[0]:
SENS[22]:70 00 05 00 00 00 00 0E 00 00 00 00 24 00 00 C8 00 01 00 00 00 00

Сравниваем с оригиналом:
CDB:08 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 STATUS: 02, LEN: 400 SENS[22]:F0 00 80 00 00 01 FE 0E 00 00 00 00 00 01 00 00 30 21 00 00 00 00

Статус совпадает, SENS совпадает. Но при работе с SG он сказал, что вернул 0x400 байт данных, а SPTI нам говорит, что данных она не получила. Интересно, посмотрим код внимательней, в поисках кто на это мог повлиять. И видим кусок:

    if (!io_hdr.status) {
        actual_len = io_hdr.dxfer_len - io_hdr.resid;
    } else {
        /* NO SENSE | ILI (Incorrect Length Indicator) */
        if (io_hdr.sbp[2] == 0x20)
            actual_len = io_hdr.dxfer_len - io_hdr.resid;
        else
            actual_len = 0;
 
        cmd->sense_len = io_hdr.sb_len_wr;

    }

Итак, если статус GOOD то читаем размер данных из того что нам вернул SG, если же нет то проверяем третий байт SENS на константу 0x20. И если это так то берем размер данных из SG, а если нет, то возвращаем 0. Смотрим наш случай и видим третий байт у нас 0x80 то есть код работает правильно. Но делает ли он то, что нужно? Посмотрим, что такое же значат все эти циферки в SENS. Самое понятное, что я смог найти, это был документ от Oracle описывающий команды LTO5(о нем может быть позже) стримера docs.oracle.com/cd/E21419_04/en/LTO5_Vol3_E5b/LTO5_Vol3_E5b.pdf



И тут мы видим что третий байт это комбинированное поле содержащее три флага:
Mark(или FMK) - при чтении блока мы встретили признак конца блока данных.
EOM - Мы писали данные, и дошли до конца носителя
ILI - Мы хотели прочитать наприме 1024 байт, а выставлен режим чтения по 512 байт.

И поля SENS CODE которое уточняет что случилось. В нашем случае оно «0», что по описанию:



И вообще это не ошибка, а просто информация.

Получаем, что выше указанный код явно не очень корректно работает. Так как флаг FMK/EOM он не обрабатывает должным образом, к тому же SENS CODE тоже игнорируется. И главный вопрос, что он вообще тут делает. Возможно, он обрабатывал какую то старую ошибку. Или возможно просто тот, кто писал не до конца разобрался с этими кодами. Но по мне код делал лишнюю работу, и искажал данные, передаваемые между SG и iSCSI.

Меняем все эти условия на такой кусок:

actual_len = io_hdr.dxfer_len - io_hdr.resid;
cmd->sense_len = io_hdr.sb_len_wr;

Компилируем, запускаем, работает.


Данные восстанавливаются. Проверил, хэш файла совпал с оригиналом. Отлично. Осталось последнее, отдать патч назад в опенсорс. Может, кому пригодится. Ну или хотя бы объяснят, почему так делать не надо, и для чего были все эти проверки. Самый простой способ оказался через гитхаб. Заслать пулл реквест автору проекта. Так и было сделано. Как не странно в этот раз всё прошло замечательно, уже на следующий день патч был принят, и смержен с основной веткой проекта. И теперь такая, казалось бы, тривиальная вещь, как проброска SCSI устройства через iSCSI стала более тривиальной.

Комментарии (10)


  1. Javian
    29.03.2022 21:41

    HP LTO-1, со SCSI контроллером и кучей картриджей небольшой юзаности

    картридж 200 Гб - ~5000 руб (по данным Я.Маркет).

    Компания HP гарантирует хранение архивных данных на картриджах LTO Ultrium в течение 30 лет и/или на протяжении 260 полных циклов резервного копирования. Этого вполне достаточно для соответствия постоянно растущим нормативным требованиям к хранению и архивации данных.

    Имеет ли смысл, если взять 4Тб HDD и положить на хранение на полку.


    1. lebfr
      29.03.2022 22:42
      +1

      Это не правильные цены. Посмотрите на авито. Я покупал стандарт LTO-3 вообще, за 50 рублей, за картридж. Покупали разом большую партию сообществом с канала Fagear. В среднем LTO-3 можно найти картридж по 300-500 рублей. Такого объёма они сейчас нужны только частным лицам.

      Про надёжность HDD лежащего на полке переубеждать не буду, но она нет так высока, как может показаться. Да, картриджи имеют свои неудобства. Но я готов с ними мириться.


    1. VBKesha Автор
      30.03.2022 11:56
      +1

      картридж 200 Гб — ~5000 руб (по данным Я.Маркет).

      Стример + SCSI контроллер со шлейфом + 20 картриджей = 2000 рублей на авито.
      И да на самом деле LTO1 это 100Gb, 200 это если будет работать встроенная компрессия, и она сможет в два раза пожать то что пишется на ленту.
      По ценам lebfr написал. LTO4 новые вполне можно найти за 500 рублей новые, LTO5 за 1000-1200.

      И тут уже математика получается интересная LTO5 я нашёл за 20к, SAS контроллер+шлейфы ещё 6к и 4 картриджа LTO4(LTO5 может читать писать 4,5 семейство, и только читать 3) ещё 2.5К получается вроде дороже винта на 2Tb но можно докупить ещё 5 картриджей и это уже получается дешевле винта на 4 Tb, по крайней мере по текущим ценам.

      Имеет ли смысл, если взять 4Тб HDD и положить на хранение на полку.

      Очень очень холиварная тема. HDD в разы удобней. Но ИМХО в надежности он очень проигрывает. Тут аргументов(наверное как и контрагрументов) на статью соберется.
      У него нету даже защелки защиты от записи :)


      1. screwer
        30.03.2022 13:01
        +1

        FC ещё дешевле может быть. Я покупал по 500 рублей HBA (4гбит, один SFP) и по 300 патчкорды. На Авито.

        FC мне нравится больше - кабель тоньше и гибче, существенно дешевле, может быть довольно длинным, гальваническая развязка, легко объединить несколько устройств кольцом.

        Правда есть и недостатки - стримеры IBM LTO5 не виделись этим адаптером. Тогда как HP,шный работал прекрасно. Пришлось купить 8гбит, два SFP. За 2500р.

        А вот SAS дисковую полку мне до сих пор не удалось подключить. Пробовал два разных SAS HBA, на разных чипсетах, в разных режимах.


  1. select26
    29.03.2022 23:00

    Интересно написано и полезно понимать методику поиска проблем.

    Порадовало имя тестового файла.

    Спасибо!


  1. DmitrySpb79
    30.03.2022 15:02

    Интересно как параллельно одни и те же идеи приходят в голову :) Только вчера купил Compaq SCSI2 streamer с DDS3 кассетами на "поиграться", но с PCI-платой не определился еще.


  1. enamchuk
    01.04.2022 12:53

    Спасибо Максу “Fagear” Крюкову, благодаря его длинным и интересным рассказам, обзорам и ремонтам я вновь заинтересовался стримерами. Сперва коллега подарил мне стример DDS-5, потом коллега себе взял SDLT, а я купил себе LTO-4, а потом удалось добыть по скромной цене и LTO-5, который использую регулярно. Картриджи удалось купить по цене 400-500 рублей за штуку, 1.5 Тбайт данных на один картридж для дома - солидный объём.

    Жаль, что в отличие от приводов CD, умеющих читать данные и аудио, стримеры DDS не умеют воспроизводить аудио DAT.


    1. vikarti
      01.04.2022 13:16

      А где картриджи брали? Авито?


      1. enamchuk
        01.04.2022 14:16

        Да, всё брал на Avito. И SAS-контроллер тоже. В моей материнской плате есть разъёмы SAS, но со встроенным контроллером стример не работал - определялся в системе, реагировал на команды, но не проходил тесты (писал, что привод неисправный, замените) и не писал кассеты.
        Пришлось брать другой HBA-контроллер, с ним LTO-5 заработал отлично.
        А LTO-4 прекрасно работает и на строенном в матплату контроллере (c603 вроде).


    1. VBKesha Автор
      01.04.2022 15:19

      Жаль, что в отличие от приводов CD, умеющих читать данные и аудио, стримеры DDS не умеют воспроизводить аудио DAT.

      Если посмотреть на историю CD. То по сути при переходе от AudioCD к DataCD, особо то ничего не меняется. Поменялся тип трека в данных субканала, и договоренность как должен выглядеть сектор с данными. Но плотность, формат кодирования и прочее остался совершенно идентичный. То у DDS я так понимаю и плотность записи менялась и материалы лент, а сними видимо и плотность дорожек. Вот такой фокус и не удался.