SCSI / iSCSI 서브시스템

Linux 커널의 SCSI 서브시스템을 심층 분석합니다. SCSI 아키텍처 모델(SAM), Upper/Mid/Lower 3계층 구조, 핵심 구조체, 명령 실행·완료 경로, 오류 처리(EH), SAS·FC·iSCSI·UAS 전송 프로토콜, iSCSI 심화(open-iscsi/LIO), SCSI 드라이버 개발, sysfs 인터페이스, 디버깅 기법을 다룹니다.

전제 조건: Block I/O 서브시스템(bio, 블록 계층 개요)과 디바이스 드라이버(디바이스 모델)를 먼저 읽으세요.
일상 비유: SCSI 서브시스템은 국제 물류 시스템과 같습니다. SCSI 명령 세트는 전 세계 공통의 화물 서류 양식(CDB)이고, SAS/FC/iSCSI 등 전송 프로토콜은 운송 수단(트럭/선박/항공)입니다. 어떤 운송 수단을 쓰든 서류 양식은 동일하므로, 상위 계층이 전송 방식을 신경 쓸 필요가 없습니다.

핵심 요약

  • SCSI — 스토리지 디바이스와 통신하는 명령 프로토콜 패밀리입니다. NVMe를 제외한 거의 모든 블록 디바이스가 경유합니다.
  • 3계층 구조 — Upper(sd, sr, sg) / Mid(SCSI 코어) / Lower(HBA 드라이버) 계층으로 분리됩니다.
  • CDB — Command Descriptor Block. SCSI 명령의 표준 형식(READ/WRITE/INQUIRY 등)입니다.
  • iSCSI — TCP/IP 위에서 SCSI 명령을 전송하는 프로토콜. 원격 스토리지를 로컬처럼 사용합니다.
  • libata — SATA 디바이스를 SCSI 명령으로 변환하여 SCSI 스택에서 처리합니다.

단계별 이해

  1. SCSI 디바이스 확인lsscsi로 시스템에 연결된 SCSI 디바이스를 확인합니다.

    SATA HDD/SSD도 libata를 통해 SCSI 디바이스로 표시됩니다.

  2. 3계층 이해 — Upper Layer(sd 드라이버)가 읽기/쓰기 요청을 SCSI 명령으로 변환하고, Mid Layer가 큐잉/에러 처리를 하며, Lower Layer(HBA)가 하드웨어에 전달합니다.

    /sys/class/scsi_host/에서 HBA 정보를 확인할 수 있습니다.

  3. SCSI 명령 관찰sg_inq /dev/sda로 INQUIRY 명령을 보내 디바이스 정보를 조회합니다.

    sg3_utils 패키지가 다양한 SCSI 명령 도구를 제공합니다.

  4. 에러 처리 — SCSI 명령 실패 시 Mid Layer의 Error Handler(EH)가 복구를 시도합니다.

    명령 재시도 → 디바이스 리셋 → 버스 리셋 → 호스트 리셋 순으로 에스컬레이션합니다.

SCSI 서브시스템 개요

SCSI(Small Computer System Interface)는 1986년 ANSI 표준(X3.131)으로 시작하여, 40년 가까이 스토리지 I/O의 핵심 프로토콜 패밀리로 진화해왔습니다. 물리 버스 규격에서 출발했지만, 오늘날에는 전송 프로토콜과 독립적인 명령 세트(Command Set)로 기능하며, SAS·Fibre Channel·iSCSI·USB(UAS) 등 다양한 전송 계층 위에서 동작합니다.

Linux 커널의 SCSI 서브시스템(drivers/scsi/)은 이 SCSI 프로토콜 패밀리 전체를 지원하는 통합 프레임워크를 제공합니다. 직접적인 SCSI 디바이스뿐 아니라, libata를 통해 SATA 디바이스도 SCSI 명령으로 변환하여 처리하므로, 사실상 NVMe를 제외한 거의 모든 블록 스토리지가 SCSI 스택을 경유합니다.

프로토콜 진화

세대전송 방식최대 대역폭최대 거리비고
Parallel SCSI (SPI)병렬 버스640 MB/s (Ultra-640)~12m레거시, 커널에서 단종
SAS (Serial Attached SCSI)직렬 점대점22.5 Gb/s (SAS-4)~10m엔터프라이즈 표준
Fibre Channel (FC)직렬 (광섬유/구리)128 Gb/s (64GFC)~10km (광)SAN 환경
iSCSITCP/IP 네트워크네트워크 대역 의존무제한IP 기반 SAN
UAS (USB Attached SCSI)USB 3.x20 Gb/s (USB 3.2)~5m외장 스토리지

SCSI 명령 세트 표준

SCSI 표준은 T10 위원회(현 INCITS)에서 관리하며, 용도별로 분리된 명령 세트로 구성됩니다:

표준대상 디바이스주요 명령
SPC (SCSI Primary Commands)모든 SCSI 디바이스 공통INQUIRY, TEST UNIT READY, REQUEST SENSE, MODE SELECT/SENSE
SBC (SCSI Block Commands)디스크 (HDD/SSD)READ(10/16), WRITE(10/16), SYNCHRONIZE CACHE, UNMAP
SSC (SCSI Stream Commands)테이프 드라이브READ, WRITE, REWIND, SPACE
MMC (Multi-Media Commands)CD/DVD/BDREAD TOC, GET CONFIGURATION, READ DISC INFORMATION
SES (SCSI Enclosure Services)디스크 인클로저RECEIVE DIAGNOSTIC, SEND DIAGNOSTIC
참고: Linux 커널에서 sd 드라이버는 SBC, sr은 MMC, st는 SSC, ses는 SES 명령 세트를 구현합니다. sg(SCSI Generic)는 사용자 공간에서 임의의 CDB를 전송할 수 있는 범용 인터페이스입니다.

SCSI 아키텍처 모델 (SAM)

SAM(SCSI Architecture Model)은 SCSI 프로토콜의 추상적 동작 모델을 정의합니다. 전송 매체와 무관하게 Initiator–Target 간 명령 전달 체계를 규격화합니다.

핵심 구성 요소

SAM 개념설명Linux 커널 대응
Initiator명령을 발행하는 주체struct Scsi_Host (HBA 드라이버)
Target명령을 수신·실행하는 주체struct scsi_target
LUN (Logical Unit)Target 내 논리적 디바이스struct scsi_device
HBA (Host Bus Adapter)호스트와 SCSI 버스를 연결하는 하드웨어struct scsi_host_template + LLD
CDB (Command Descriptor Block)SCSI 명령 패킷 (6/10/12/16/32바이트)scsi_cmnd.cmnd[]
Sense Data오류/상태 상세 정보scsi_cmnd.sense_buffer[]

SCSI 명령 모델

SCSI 명령은 CDB(Command Descriptor Block)라는 고정/가변 길이 패킷으로 구성됩니다. CDB의 첫 바이트가 Operation Code이며, 명령 길이를 결정합니다:

/* CDB 구조 예시: READ(10) — 10바이트 CDB */
/*  Byte 0: Operation Code (0x28 = READ(10))  */
/*  Byte 1: [7:5] Reserved, [4:0] Service Action (보통 0) */
/*  Byte 2-5: Logical Block Address (빅엔디안)   */
/*  Byte 6: Reserved / Group Number              */
/*  Byte 7-8: Transfer Length (블록 수, 빅엔디안) */
/*  Byte 9: Control                               */

u8 cdb[10] = {
    0x28,                           /* READ(10) */
    0x00,                           /* flags */
    (lba >> 24) & 0xff,           /* LBA [31:24] */
    (lba >> 16) & 0xff,           /* LBA [23:16] */
    (lba >> 8)  & 0xff,           /* LBA [15:8]  */
    lba         & 0xff,           /* LBA [7:0]   */
    0x00,                           /* group */
    (len >> 8)  & 0xff,           /* length MSB */
    len         & 0xff,           /* length LSB */
    0x00                            /* control */
};

SCSI 상태 코드(Status Byte)는 명령 완료 시 반환됩니다:

상태 코드의미
GOOD0x00명령 성공
CHECK CONDITION0x02오류 발생 — Sense Data 확인 필요
BUSY0x08Target 처리 중, 재시도 필요
RESERVATION CONFLICT0x18다른 Initiator가 예약한 리소스
TASK SET FULL0x28큐 포화, 나중에 재시도
ACA ACTIVE0x30자동 비상 연합 활성
Initiator CDB Target Data-Out Data-In LUN (디바이스) Status + Sense Data ① Command → ② Data → ③ Status

Linux SCSI 스택 구조

Linux SCSI 서브시스템은 3계층으로 구성됩니다. Upper Layer가 블록/문자 디바이스 인터페이스를 제공하고, Mid Layer(scsi_mod)가 명령 관리·큐잉·오류 처리를 담당하며, Lower Layer의 LLD(Low-Level Driver)가 실제 HBA 하드웨어를 제어합니다.

Block Layer (blk-mq) Upper Layer sd (disk) sr (cdrom) st (tape) sg (generic) ses Mid Layer (scsi_mod) 명령 관리 · 큐잉 · 오류 처리(EH) · 장치 스캔 · 전송 클래스 Lower Layer (LLD) mpt3sas megaraid_sas hpsa libata (SATA) iscsi_tcp / qla2xxx HBA Hardware / Network

Upper Layer 드라이버

드라이버모듈디바이스 노드용도
sdsd_mod/dev/sd[a-z]디스크 (HDD, SSD, SAS, USB 스토리지)
srsr_mod/dev/sr[0-9]CD/DVD/Blu-ray
stst/dev/st[0-9]테이프 드라이브
sgsg/dev/sg[0-9]SCSI Generic — 사용자 공간에서 직접 CDB 전송
sessesSES 인클로저 서비스

SCSI Mid Layer (scsi_mod)

커널 모듈 scsi_mod는 SCSI 서브시스템의 핵심입니다. 주요 책임:

Lower Layer — LLD (Low-Level Driver)

LLDHBA / 전송벤더
mpt3sasSAS 3xxx HBABroadcom (LSI)
megaraid_sasMegaRAID SASBroadcom (LSI)
hpsa / smartpqiSmart Array / SmartPQIHPE (Microchip)
aacraidAdaptec RAIDMicrosemi
qla2xxxFibre ChannelQLogic (Marvell)
lpfcFibre ChannelEmulex (Broadcom)
iscsi_tcpiSCSI (소프트웨어)Open-iSCSI
libata + ahciSATA → SCSI 변환(표준)
uasUSB Attached SCSI(표준)
scsi_debug가상 SCSI 디바이스테스트/개발용

핵심 구조체

SCSI 서브시스템의 동작을 이해하려면 4가지 핵심 구조체를 알아야 합니다.

scsi_host_template

LLD가 Mid Layer에 자신의 능력(capabilities)과 콜백을 알리는 템플릿입니다. scsi_host_alloc() 호출 시 이 템플릿을 기반으로 Scsi_Host가 생성됩니다.

/* include/scsi/scsi_host.h — 핵심 필드만 발췌 */
struct scsi_host_template {
    const char  *name;              /* 드라이버 이름 */
    const char  *proc_name;         /* /proc/scsi/ 디렉터리명 */

    /* ── 필수 콜백 ── */
    int  (*queuecommand)(struct Scsi_Host *, struct scsi_cmnd *);
    /* CDB를 HBA에 제출 — 반드시 구현 */

    /* ── 오류 처리 콜백 ── */
    int  (*eh_abort_handler)(struct scsi_cmnd *);
    int  (*eh_device_reset_handler)(struct scsi_cmnd *);
    int  (*eh_target_reset_handler)(struct scsi_cmnd *);
    int  (*eh_host_reset_handler)(struct scsi_cmnd *);

    /* ── 디바이스 수명 주기 ── */
    int  (*slave_alloc)(struct scsi_device *);
    int  (*slave_configure)(struct scsi_device *);
    void (*slave_destroy)(struct scsi_device *);

    /* ── 큐 제한 ── */
    int  can_queue;                /* HBA가 동시에 처리 가능한 최대 명령 수 */
    int  cmd_per_lun;              /* LUN당 최대 동시 명령 */
    short sg_tablesize;            /* scatter-gather 세그먼트 최대 수 */
    unsigned short max_sectors;    /* 단일 명령 최대 섹터 수 */

    struct module *module;
};

Scsi_Host

HBA 인스턴스를 나타내며, scsi_host_alloc()으로 할당됩니다. LLD는 이 구조체의 hostdata[] 영역에 private 데이터를 저장합니다.

/* include/scsi/scsi_host.h — 핵심 필드만 발췌 */
struct Scsi_Host {
    struct scsi_host_template  *hostt;       /* 템플릿 포인터 */
    struct list_head           __devices;    /* scsi_device 리스트 */
    struct list_head           __targets;    /* scsi_target 리스트 */

    unsigned int  host_no;                   /* 고유 호스트 번호 */
    unsigned int  max_id;                     /* 최대 Target ID */
    unsigned int  max_lun;                    /* 최대 LUN */
    unsigned int  max_channel;                /* 최대 채널 */
    unsigned int  can_queue;                  /* 동시 명령 제한 */

    struct device  shost_dev;                /* sysfs 장치 */
    struct workqueue_struct  *tmf_work_q;   /* Task Management 워크큐 */

    unsigned long  hostdata[0]               /* LLD private data */
        __attribute__((aligned(sizeof(unsigned long))));
};

scsi_device

SCSI LUN 하나를 나타냅니다. 디바이스 스캔 시 INQUIRY 응답을 기반으로 Mid Layer가 생성합니다.

/* include/scsi/scsi_device.h — 핵심 필드만 발췌 */
struct scsi_device {
    struct Scsi_Host      *host;
    struct request_queue  *request_queue;
    struct scsi_target    *sdev_target;

    unsigned int  id;                      /* Target ID */
    u64           lun;                     /* Logical Unit Number */
    unsigned int  channel;                 /* SCSI 채널 */

    unsigned      sector_size;             /* 논리 섹터 크기 (보통 512) */
    unsigned char type;                    /* SCSI 디바이스 타입 (TYPE_DISK 등) */
    unsigned char scsi_level;              /* SCSI 버전 */
    char          vendor[8];               /* INQUIRY 벤더 */
    char          model[16];               /* INQUIRY 모델 */
    char          rev[4];                  /* INQUIRY 리비전 */

    unsigned int  queue_depth;             /* 동시 명령 큐 깊이 */
    struct device sdev_gendev;              /* sysfs 장치 */
};

scsi_cmnd

하나의 SCSI 명령을 나타내는 구조체입니다. blk-mq의 request에 내장(embed)되어 할당됩니다.

/* include/scsi/scsi_cmnd.h — 핵심 필드만 발췌 */
struct scsi_cmnd {
    struct scsi_device   *device;
    struct request       *request;         /* blk-mq request */

    unsigned char  cmnd[32];               /* CDB 버퍼 (최대 32바이트) */
    unsigned short cmd_len;                 /* CDB 길이 */

    unsigned int   result;                  /* 완료 상태 (host_byte | msg_byte | status_byte) */
    unsigned char  sense_buffer[96];        /* Sense Data */

    enum dma_data_direction  sc_data_direction;
    unsigned int   transfersize;            /* 전송 블록 크기 */
    struct scsi_data_buffer  sdb;           /* scatter-gather 데이터 */

    unsigned int   allowed;                 /* 최대 재시도 횟수 */
    int            retries;                 /* 현재 재시도 횟수 */
};

SCSI 명령 실행 경로

제출 경로 (Submit Path)

블록 I/O가 SCSI 명령으로 변환되어 HBA에 도달하기까지의 경로입니다:

  1. submit_bio() → blk-mq 소프트웨어 큐에 bio 삽입
  2. blk-mq가 bio를 struct request로 병합·변환
  3. scsi_queue_rq() — SCSI blk-mq ops의 .queue_rq 콜백
  4. scsi_dispatch_cmd() — CDB 구성, scsi_cmnd 준비
  5. hostt->queuecommand() — LLD에 명령 전달
  6. LLD가 HBA DMA 레지스터에 명령을 기록하거나 네트워크로 전송

완료 경로 (Completion Path)

HBA가 명령 완료를 통지하면 다음 경로로 처리됩니다:

  1. HBA 인터럽트 → LLD ISR 실행
  2. LLD가 scsi_done(scsi_cmnd) 호출
  3. scsi_decide_disposition() — 상태 판단 (성공/재시도/EH)
  4. 성공 시: scsi_finish_command()blk_mq_complete_request()
  5. 오류 시: scsi_eh_scmd_add() → EH 큐에 추가
Block Layer SCSI Mid LLD HBA scsi_queue_rq() queuecommand() DMA / Network ── 완료 경로 ── IRQ → ISR scsi_done() scsi_decide_disposition() blk_mq_complete_request() 제출 경로 완료 경로

blk-mq와 SCSI 연동

Linux 5.0+ 이후 모든 SCSI 드라이버는 blk-mq를 사용합니다. SCSI Mid Layer는 scsi_mq_ops를 blk-mq에 등록합니다:

/* drivers/scsi/scsi_lib.c */
static const struct blk_mq_ops scsi_mq_ops = {
    .queue_rq       = scsi_queue_rq,
    .complete       = scsi_complete,
    .timeout        = scsi_timeout,
    .init_request   = scsi_mq_init_request,
    .exit_request   = scsi_mq_exit_request,
    .map_queues     = scsi_map_queues,
};

/* scsi_cmnd는 blk-mq request의 driver private 영역에 내장됨 */
struct scsi_cmnd *scsi_cmd = blk_mq_rq_to_pdu(rq);
팁: can_queue는 HBA가 동시에 처리 가능한 명령 수를 결정하며, blk-mq의 queue_depth에 직접 영향을 줍니다. NVMe 수준의 높은 동시성이 필요한 SAS HBA에서는 이 값을 수천 이상으로 설정합니다.

SCSI 오류 처리 (EH)

SCSI 오류 처리(Error Handling)는 타임아웃이나 CHECK CONDITION 등 오류 발생 시 에스컬레이션 단계별 복구를 시도하는 메커니즘입니다. 커널 스레드 scsi_eh_[N]가 EH를 담당합니다.

① Abort eh_abort_handler 실패 ② Device Reset eh_device_reset 실패 ③ Target Reset eh_target_reset 실패 ④ Host Reset eh_host_reset 성공 → 명령 재시도 실패 → 디바이스 오프라인 점점 넓은 범위를 리셋하며 복구를 시도 — 최소 영향 원칙

EH 동작 흐름

  1. 타임아웃 감지: blk-mq 타이머가 scsi_timeout()을 호출
  2. EH 큐 등록: scsi_eh_scmd_add()가 실패한 명령을 EH 목록에 추가
  3. EH 스레드 기상: scsi_eh_[N] 커널 스레드가 깨어남
  4. 에스컬레이션: abort → device reset → target reset → host reset 순서로 시도
  5. 복구 완료: 성공한 단계에서 실패 명령을 재시도하거나, 모든 단계 실패 시 디바이스를 오프라인으로 전환
/* drivers/scsi/scsi_error.c — EH 에스컬레이션 핵심 로직 */
static void scsi_unjam_host(struct Scsi_Host *shost)
{
    /* 1단계: 명령 abort 시도 */
    if (!scsi_eh_abort_cmds(&eh_work_q, &done_q))
        goto done;

    /* 2단계: 디바이스 리셋 */
    if (!scsi_eh_bus_device_reset(shost, &eh_work_q, &done_q))
        goto done;

    /* 3단계: 타겟 리셋 */
    if (!scsi_eh_target_reset(shost, &eh_work_q, &done_q))
        goto done;

    /* 4단계: 호스트 리셋 (가장 파괴적) */
    scsi_eh_bus_reset(shost, &eh_work_q, &done_q);
    scsi_eh_host_reset(shost, &eh_work_q, &done_q);

done:
    scsi_eh_flush_done_q(&done_q);  /* 복구된 명령 재발행 */
}

Sense Data 해석

CHECK CONDITION 상태 시 Target이 반환하는 Sense Data는 3단계 키로 오류를 분류합니다:

Sense Key의미대표적 원인
NO SENSE0x0오류 없음정보성 응답
RECOVERED ERROR0x1복구된 오류ECC 교정 성공
NOT READY0x2디바이스 준비 안 됨스핀업 중, 매체 없음
MEDIUM ERROR0x3매체 오류불량 섹터, 읽기 불가
HARDWARE ERROR0x4하드웨어 오류컨트롤러 고장
ILLEGAL REQUEST0x5잘못된 명령/파라미터지원하지 않는 CDB
UNIT ATTENTION0x6상태 변경 알림매체 교체, 리셋 발생
DATA PROTECT0x7쓰기 보호읽기 전용 매체
ABORTED COMMAND0xB명령 중단패리티 오류, 타임아웃
주의: UNIT ATTENTION은 오류가 아닌 알림입니다. 매체 교체, 버스 리셋 후 첫 명령에서 발생하며, 보통 자동 재시도로 해결됩니다. 하지만 반복 발생 시 하드웨어 문제를 의심해야 합니다.

SCSI 전송 프로토콜

SCSI 명령은 다양한 물리/네트워크 전송 계층을 통해 전달됩니다. Linux 커널은 각 전송 방식에 대한 전송 클래스(Transport Class)를 sysfs로 노출합니다.

전송매체최대 대역폭최대 거리주요 용도커널 모듈
SAS직렬 구리22.5 Gb/s~10m서버/DASscsi_transport_sas
FC광섬유/구리128 Gb/s~10kmSANscsi_transport_fc
iSCSITCP/IP네트워크 의존무제한IP SANscsi_transport_iscsi
UASUSB20 Gb/s~5m외장uas
SATA직렬 구리6 Gb/s~1m데스크톱/서버libata + ahci

SAS (Serial Attached SCSI)

엔터프라이즈 스토리지의 표준 전송입니다. Expander를 통한 팬아웃, 듀얼 포트, SATA 디바이스 호환이 특징입니다. 커널에서는 scsi_transport_sas 클래스가 SAS 주소(WWN), PHY 속성, Expander 토폴로지를 sysfs에 노출합니다.

Fibre Channel (FC)

대규모 SAN(Storage Area Network) 환경에서 사용됩니다. FC 스위치를 통한 Fabric 토폴로지가 일반적이며, WWNN/WWPN으로 노드와 포트를 식별합니다. scsi_transport_fc 클래스가 포트 상태, 속도, 통계를 관리합니다.

iSCSI (개요)

TCP/IP 네트워크 위에서 SCSI 명령을 전달합니다. 기존 이더넷 인프라를 활용할 수 있어 비용 효율적입니다. 자세한 내용은 iSCSI 심화 섹션을 참조하세요.

UAS (USB Attached SCSI)

USB BOT(Bulk-Only Transport)를 대체하는 프로토콜로, 명령 큐잉과 스트림 파이프를 지원하여 USB 스토리지 성능을 크게 향상시킵니다. uas 모듈이 담당합니다.

SATA (libata)

SATA 디바이스는 원래 ATA 프로토콜을 사용하지만, Linux에서는 libata가 ATA 명령을 SCSI 명령으로 변환(SAT — SCSI-ATA Translation)하여 SCSI 스택을 그대로 활용합니다. 따라서 SATA HDD/SSD도 /dev/sd*로 나타납니다.

iSCSI 심화

iSCSI(Internet Small Computer System Interface)는 TCP/IP 네트워크를 통해 SCSI 명령을 캡슐화하여 전송하는 프로토콜입니다. RFC 3720(2004)에서 정의되었으며, RFC 7143(2014)에서 통합 개정되었습니다.

iSCSI 프로토콜 기초

iSCSI는 기존 이더넷 인프라 위에서 블록 스토리지 접근을 제공하므로, Fibre Channel 대비 낮은 비용으로 SAN을 구축할 수 있습니다. 주요 개념:

PDU 구조

모든 iSCSI 통신은 PDU 단위로 이루어집니다. PDU는 48바이트 BHS(Basic Header Segment)로 시작합니다:

PDU 타입Opcode방향설명
Login Request/Response0x03/0x23I↔T인증 및 세션 협상
SCSI Command0x01I→TCDB 전달
SCSI Response0x21T→I명령 완료 상태
Data-Out / Data-In0x05/0x25I→T / T→I데이터 전송
NOP-Out / NOP-In0x00/0x20I↔T연결 유지 (keepalive)
Text Request/Response0x04/0x24I↔TDiscovery, 파라미터 교환
Logout Request/Response0x06/0x26I↔T세션/연결 종료
TMF (Task Mgmt)0x02/0x22I→T태스크 abort/리셋

세션 관리

iSCSI 세션은 3단계로 수립됩니다:

Initiator Target ① Discovery SendTargets (Text Request) Target IQN + Portal 목록 ② Login Login Request (CHAP 인증, 파라미터 협상) Login Response (TSIH, MaxBurstLength 등) ③ Full Feature SCSI Command PDU (CDB) Data-In PDU + SCSI Response PDU TCP 3260 포트 (또는 커스텀 포트)

Linux iSCSI Initiator — open-iscsi

open-iscsi는 Linux의 표준 iSCSI Initiator 구현입니다. 커널 모듈(iscsi_tcp, libiscsi)과 사용자 공간 데몬(iscsid)으로 구성됩니다.

# 1. Target 검색 (Discovery)
$ iscsiadm -m discovery -t sendtargets -p 192.168.1.100:3260
192.168.1.100:3260,1 iqn.2024-01.com.example:storage.lun0

# 2. Target에 로그인 (세션 수립)
$ iscsiadm -m node -T iqn.2024-01.com.example:storage.lun0 \
    -p 192.168.1.100:3260 --login

# 3. 세션 상태 확인
$ iscsiadm -m session -P 3
Target: iqn.2024-01.com.example:storage.lun0
    Current Portal: 192.168.1.100:3260,1
    Attached scsi disk sdb   State: running

# 4. 자동 로그인 설정 (부팅 시 자동 연결)
$ iscsiadm -m node -T iqn.2024-01.com.example:storage.lun0 \
    -p 192.168.1.100:3260 --op update \
    -n node.startup -v automatic

# 5. 로그아웃
$ iscsiadm -m node -T iqn.2024-01.com.example:storage.lun0 \
    -p 192.168.1.100:3260 --logout

Linux iSCSI Target — LIO

LIO(Linux-IO Target)는 커널에 내장된 iSCSI Target 프레임워크입니다. targetcli를 사용하여 설정합니다.

# targetcli를 사용한 iSCSI Target 설정 예제

# 1. 블록 디바이스를 백스토어로 등록
$ targetcli
/> /backstores/block create myblock /dev/sdc

# 2. iSCSI Target 생성
/> /iscsi create iqn.2024-01.com.example:storage.lun0

# 3. LUN 매핑
/> /iscsi/iqn.2024-01.com.example:storage.lun0/tpg1/luns \
    create /backstores/block/myblock

# 4. ACL 설정 (Initiator IQN 허용)
/> /iscsi/iqn.2024-01.com.example:storage.lun0/tpg1/acls \
    create iqn.2024-01.com.example:client1

# 5. 포털 확인 (기본 0.0.0.0:3260)
/> /iscsi/iqn.2024-01.com.example:storage.lun0/tpg1/portals ls

# 6. 설정 저장
/> saveconfig
/> exit
팁: LIO는 iSCSI 외에도 FC(tcm_fc), SRP(ib_srpt), vhost(tcm_vhost) 등 다양한 fabric을 지원하는 통합 Target 프레임워크입니다. /sys/kernel/config/target/에서 configfs로 직접 설정도 가능합니다.

iSER (iSCSI Extensions for RDMA)

iSER은 InfiniBand/RoCE RDMA 전송 위에서 iSCSI를 실행하여, TCP 오버헤드를 제거하고 제로카피 전송을 가능하게 합니다. ib_iser(Initiator) 및 ib_isert(Target) 모듈이 담당합니다. 일반 iSCSI 대비 대역폭과 레이턴시에서 큰 이점이 있으며, HPC/AI 스토리지에서 활용됩니다.

iSCSI 성능 튜닝

파라미터기본값권장값 (10GbE+)설명
node.session.cmds_max128256–1024세션당 최대 동시 명령
node.session.queue_depth3264–256LUN당 큐 깊이
node.conn[0].iscsi.MaxRecvDataSegmentLength262144262144최대 수신 데이터 세그먼트 (바이트)
node.conn[0].iscsi.FirstBurstLength65536262144비솔리시티드(unsolicited) 데이터 최대 크기
node.conn[0].iscsi.MaxBurstLength1677619216776192단일 Data-Out 시퀀스 최대 크기
node.session.nr_sessions12–4멀티세션 (MC/S)
# 파라미터 조정 예시
$ iscsiadm -m node -T iqn.2024-01.com.example:storage.lun0 \
    --op update -n node.session.queue_depth -v 128
$ iscsiadm -m node -T iqn.2024-01.com.example:storage.lun0 \
    --op update -n node.session.cmds_max -v 512

# Jumbo Frame 활성화 (네트워크 측)
$ ip link set eth0 mtu 9000
경고: 프로덕션 환경에서 iSCSI를 사용할 때 반드시 CHAP 인증을 설정하세요. 인증 없이 운영하면 네트워크에 접근 가능한 누구나 스토리지에 접근할 수 있습니다. 또한 스토리지 트래픽을 별도 VLAN으로 분리하는 것을 권장합니다.

SCSI 드라이버 스켈레톤

최소한의 가상 SCSI HBA 드라이버 예제입니다. 실제 하드웨어 대신 요청을 즉시 완료하는 구조입니다.

#include <linux/module.h>
#include <linux/kernel.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>

#define DRIVER_NAME   "my_scsi_hba"
#define MAX_CMDS      64
#define MAX_SECTORS   1024
#define SG_TABLESIZE  128

/* ── queuecommand: CDB를 HBA에 제출 ── */
static int my_queuecommand(struct Scsi_Host *shost,
                            struct scsi_cmnd *cmd)
{
    /* 실제 드라이버: DMA 매핑 → HBA 레지스터/메일박스에 CDB 기록 */
    /* 여기서는 즉시 완료 시뮬레이션 */
    cmd->result = (DID_OK << 16);
    scsi_done(cmd);
    return 0;
}

/* ── EH 콜백 ── */
static int my_eh_abort(struct scsi_cmnd *cmd)
{
    dev_warn(&cmd->device->sdev_gendev, "aborting cmd\n");
    return SUCCESS;
}

static int my_eh_host_reset(struct scsi_cmnd *cmd)
{
    dev_warn(&cmd->device->sdev_gendev, "host reset\n");
    return SUCCESS;
}

/* ── Host Template ── */
static struct scsi_host_template my_sht = {
    .module                 = THIS_MODULE,
    .name                   = DRIVER_NAME,
    .proc_name              = DRIVER_NAME,
    .queuecommand           = my_queuecommand,
    .eh_abort_handler       = my_eh_abort,
    .eh_host_reset_handler  = my_eh_host_reset,
    .can_queue              = MAX_CMDS,
    .this_id                = -1,
    .sg_tablesize           = SG_TABLESIZE,
    .max_sectors            = MAX_SECTORS,
    .cmd_per_lun            = 32,
};

static struct Scsi_Host *my_shost;

static int __init my_scsi_init(void)
{
    my_shost = scsi_host_alloc(&my_sht, 0);
    if (!my_shost)
        return -ENOMEM;

    if (scsi_add_host(my_shost, NULL)) {
        scsi_host_put(my_shost);
        return -ENODEV;
    }
    scsi_scan_host(my_shost);
    pr_info("my_scsi_hba: loaded\n");
    return 0;
}

static void __exit my_scsi_exit(void)
{
    scsi_remove_host(my_shost);
    scsi_host_put(my_shost);
    pr_info("my_scsi_hba: unloaded\n");
}

module_init(my_scsi_init);
module_exit(my_scsi_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Minimal SCSI HBA skeleton driver");

sysfs 인터페이스

SCSI 서브시스템은 sysfs를 통해 풍부한 디바이스 정보와 제어 인터페이스를 제공합니다.

경로설명
/sys/class/scsi_host/host<N>/HBA 속성 (scan, state, proc_name 등)
/sys/class/scsi_device/<H:C:T:L>/SCSI 디바이스 속성
/sys/block/sd<X>/device/블록 디바이스에서 SCSI 디바이스로의 링크
/sys/class/scsi_disk/SCSI 디스크 클래스
/sys/class/iscsi_session/iSCSI 세션 속성
/sys/class/iscsi_connection/iSCSI 연결 속성
/sys/class/sas_phy/SAS PHY 속성
/sys/class/fc_host/FC HBA 포트 속성
# H:C:T:L 형식으로 디바이스 식별 (Host:Channel:Target:LUN)

# HBA 재스캔 — 새로 추가된 디바이스 탐지
$ echo "- - -" > /sys/class/scsi_host/host0/scan

# 특정 LUN 재스캔
$ echo "0 0 1" > /sys/class/scsi_host/host0/scan  # Channel:0, Target:0, LUN:1

# 디바이스 큐 깊이 조정
$ echo 64 > /sys/block/sda/device/queue_depth

# 디바이스 안전 제거 (hot-remove)
$ echo 1 > /sys/block/sdb/device/delete

# SCSI 디바이스 상태 확인
$ cat /sys/block/sda/device/state    # running / offline / blocked
$ cat /sys/block/sda/device/vendor
$ cat /sys/block/sda/device/model

디버깅 도구

lsscsi

# 모든 SCSI 디바이스 목록
$ lsscsi
[0:0:0:0]    disk    ATA      SAMSUNG MZ7LH1T9 404Q  /dev/sda
[2:0:0:0]    disk    LIO-ORG  block0           4.0   /dev/sdb

# 상세 정보 (-g: sg 디바이스, -t: 전송, -L: LUN 정보)
$ lsscsi -gtvL
# HBA 호스트 정보
$ lsscsi -H

sg3_utils

# INQUIRY 명령 전송
$ sg_inq /dev/sda
    Vendor identification: ATA
    Product identification: SAMSUNG MZ7LH1T9
    Product revision level: 404Q

# 디바이스 상태 확인 (TEST UNIT READY)
$ sg_turs /dev/sda

# Sense Data 직접 조회
$ sg_requests /dev/sda

# 로그 페이지 조회 (에러 카운터 등)
$ sg_logs /dev/sda

# SMART/Health 정보 (SBC)
$ sg_sat_identify /dev/sda

커널 로깅

# SCSI 로깅 레벨 설정 (비트마스크)
# 0x1=error, 0x2=timeout, 0x4=scan, 0x8=mlqueue, 0x10=mlcomplete
# 0x20=llqueue, 0x40=llcomplete, 0x80=hlqueue, 0x100=hlcomplete
$ echo 0x1ff > /proc/sys/dev/scsi/logging_level

# scsi_debug 가상 디바이스로 테스트
$ modprobe scsi_debug num_tgts=1 max_luns=2 dev_size_mb=256
$ lsscsi | grep scsi_debug

# 커널 메시지 필터링
$ dmesg | grep -i scsi
$ journalctl -k --grep="scsi|sd |sr "

/proc/scsi

# SCSI 서브시스템 요약
$ cat /proc/scsi/scsi
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
  Vendor: ATA      Model: SAMSUNG MZ7LH1T9 Rev: 404Q
  Type:   Direct-Access                    ANSI  SCSI revision: 05

# 특정 HBA 드라이버 정보
$ ls /proc/scsi/
$ cat /proc/scsi/sg/devices

주요 Kconfig 옵션 및 소스 구조

주요 Kconfig 옵션

옵션모듈설명
CONFIG_SCSIscsi_modSCSI 서브시스템 핵심 (필수)
CONFIG_BLK_DEV_SDsd_modSCSI 디스크 (sd) 드라이버
CONFIG_CHR_DEV_SGsgSCSI Generic 인터페이스
CONFIG_CHR_DEV_STstSCSI 테이프 드라이버
CONFIG_BLK_DEV_SRsr_modSCSI CD-ROM 드라이버
CONFIG_SCSI_SAS_ATTRSscsi_transport_sasSAS 전송 클래스
CONFIG_SCSI_FC_ATTRSscsi_transport_fcFC 전송 클래스
CONFIG_ISCSI_TCPiscsi_tcp소프트웨어 iSCSI Initiator
CONFIG_ISCSI_TARGETiscsi_target_modLIO iSCSI Target
CONFIG_SCSI_DEBUGscsi_debug가상 SCSI 디바이스 (테스트)
CONFIG_SATA_AHCIahciAHCI SATA 컨트롤러

커널 소스 디렉터리 구조

경로내용
drivers/scsi/SCSI 서브시스템 핵심 + LLD 드라이버
drivers/scsi/scsi_lib.c블록 계층 연동, scsi_mq_ops
drivers/scsi/scsi_error.c오류 처리(EH) 로직
drivers/scsi/scsi_scan.c디바이스 스캔/탐지
drivers/scsi/scsi_sysfs.csysfs 인터페이스
drivers/scsi/sd.cSCSI 디스크 (sd) 드라이버
drivers/scsi/sg.cSCSI Generic 드라이버
drivers/scsi/scsi_transport_*.c전송 클래스 (SAS, FC, iSCSI, SRP)
drivers/target/LIO Target 프레임워크
drivers/target/iscsi/iSCSI Target 구현
drivers/ata/libata + AHCI/SATA 드라이버
include/scsi/SCSI 헤더 파일