ethtool 서브시스템 심화

ethtool은 리눅스 네트워크 인터페이스의 하드웨어 수준 설정과 진단을 위한 핵심 도구이자 커널 서브시스템입니다. 사용자 공간의 ethtool 명령은 커널의 ethtool_ops 콜백 테이블을 통해 NIC 드라이버와 직접 상호작용합니다. 링크 속도/듀플렉스 설정, 링 버퍼 크기 조정, 인터럽트 코얼레싱, 오프로드 기능 제어, RSS(Receive Side Scaling), 채널 매핑, 하드웨어 통계, 자기 진단(Self-Test), EEPROM 접근, EEE(Energy Efficient Ethernet), 그리고 최신 Netlink 기반 인터페이스까지 ethtool 서브시스템의 전체 아키텍처와 드라이버 구현 방법을 상세히 다룹니다.

전제 조건: Network Device 드라이버(net_device)네트워크 스택 고급 문서를 먼저 읽으세요. ethtool은 net_device 구조체와 NIC 드라이버 인터페이스에 대한 이해가 필수입니다.
일상 비유: ethtool은 자동차의 OBD-II 진단 포트와 비슷합니다. 외부 도구(ethtool 명령)를 연결하여 엔진(NIC 하드웨어)의 상태를 확인하고, 연료 분사량(링 버퍼), 변속 타이밍(코얼레싱), 보조 기능(오프로드)을 세밀하게 조정할 수 있습니다.

핵심 요약

  • ethtool_ops -- NIC 드라이버가 구현하는 콜백 함수 테이블. 약 80개 이상의 연산을 정의합니다.
  • ioctl vs Netlink -- 기존 ioctl(SIOCETHTOOL) 방식에서 Netlink(NETLINK_GENERIC / ethnl) 방식으로 전환 중입니다.
  • 링크 설정 -- 속도, 듀플렉스, 자동 협상, FEC 등 물리 계층 매개변수를 제어합니다.
  • 성능 튜닝 -- 링 버퍼, 코얼레싱, RSS, 채널, 오프로드를 조합하여 최적 성능을 달성합니다.
  • 진단/모니터링 -- 통계, 자기 진단, EEPROM, 레지스터 덤프로 하드웨어 문제를 파악합니다.

단계별 이해

  1. 아키텍처 파악
    사용자 공간 ethtool 명령이 커널 ethtool_ops를 거쳐 드라이버에 도달하는 경로를 이해합니다.
  2. ethtool_ops 구조체 분석
    콜백 함수 테이블의 각 멤버가 어떤 ethtool 명령에 매핑되는지 파악합니다.
  3. 링크/링 버퍼/코얼레싱 설정
    가장 빈번하게 사용하는 3대 튜닝 항목의 내부 동작을 추적합니다.
  4. Netlink 인터페이스 학습
    새로운 Netlink 기반 ethtool API의 구조와 기존 ioctl과의 차이점을 확인합니다.
  5. 드라이버 구현 실습
    실제 드라이버에서 ethtool_ops를 등록하고 콜백을 구현하는 패턴을 익힙니다.

ethtool 개요

ethtool이란

ethtool은 리눅스 시스템에서 이더넷 네트워크 인터페이스 카드(NIC)의 하드웨어 수준 매개변수를 조회하고 설정하기 위한 표준 도구이자 커널 서브시스템입니다. 1999년 David S. Miller가 초기 프레임워크를 설계한 이래, 리눅스 네트워크 스택에서 가장 널리 사용되는 NIC 관리 인터페이스로 자리 잡았습니다.

ethtool 서브시스템은 크게 세 가지 계층으로 구성됩니다:

주요 기능 카테고리

카테고리ethtool 옵션커널 콜백설명
드라이버/하드웨어 정보 ethtool -i get_drvinfo() 드라이버 이름, 버전, 펌웨어 버전, 버스 정보
링크 설정 ethtool -s get/set_link_ksettings() 속도, 듀플렉스, 자동 협상, 광고 모드
링 버퍼 ethtool -g/-G get/set_ringparam() RX/TX 링 버퍼 크기
코얼레싱 ethtool -c/-C get/set_coalesce() 인터럽트 통합 타이머, 패킷 수 기반 임계값
오프로드 ethtool -k/-K get/set_features() 체크섬, TSO, GSO, GRO, LRO 등
채널 ethtool -l/-L get/set_channels() RX/TX/결합 큐 수, 기타 큐 수
RSS ethtool -x/-X get/set_rxfh() 인디렉션 테이블, 해시 키, 해시 함수
통계 ethtool -S get_ethtool_stats() NIC 하드웨어 통계 카운터
자기 진단 ethtool -t self_test() NIC 하드웨어 자체 진단 실행
EEPROM ethtool -e/-E get/set_eeprom() NIC EEPROM/Flash 읽기/쓰기
일시 중지 프레임 ethtool -a/-A get/set_pauseparam() Flow Control 일시 중지 매개변수
EEE ethtool --show-eee/--set-eee get/set_eee() 에너지 효율 이더넷 설정
레지스터 덤프 ethtool -d get_regs() NIC 하드웨어 레지스터 원시 덤프
N-tuple 필터 ethtool -n/-N get/set_rxnfc() 수신 필터 규칙(Flow Director, RFS)
FEC ethtool --show-fec/--set-fec get/set_fecparam() Forward Error Correction 모드

ethtool 커널 버전별 주요 발전 이력

커널 버전주요 변화영향
2.4.x (2001) ethtool 프레임워크 최초 도입, SIOCETHTOOL ioctl NIC 설정을 위한 표준 인터페이스 확립
2.6.x (2003) ethtool_ops 구조체 도입, 드라이버별 콜백 표준화 모든 드라이버가 통일된 API를 사용
3.0 (2011) get_rxfh_indir() RSS 인디렉션 테이블 API 멀티큐 NIC 수신 분산 세밀 제어
4.6 (2016) link_ksettings API 도입 (기존 ethtool_cmd 대체) 25G/50G/100G+ 링크 속도 지원
4.20 (2018) FEC(Forward Error Correction) API 추가 고속 이더넷 오류 정정 설정
5.6 (2020) Netlink 기반 ethtool API(ethnl) 도입 비동기 알림, 확장 가능한 새 인터페이스
5.10 (2020) per-queue 코얼레싱, 통계 API 확장 큐별 세밀한 튜닝
6.x (2022~) RSS 컨텍스트, PSE(Power Sourcing Equipment), PLCA(PHY-Level Collision Avoidance) 추가 최신 이더넷 표준 기능 지원

ethtool 아키텍처

전체 시스템 계층 구조

ethtool 서브시스템은 사용자 공간에서 하드웨어까지 네 개의 계층을 관통합니다. 사용자 도구가 ioctl 또는 Netlink 소켓을 통해 커널에 요청을 전달하면, 커널의 ethtool 코어가 요청을 검증하고 해당 NIC 드라이버의 콜백 함수를 호출합니다. 드라이버는 하드웨어 레지스터를 직접 조작하거나 펌웨어 명령을 실행하여 결과를 반환합니다.

사용자 공간 (User Space) ethtool CLI /usr/sbin/ethtool iproute2 / NM ip link, nmcli 사용자 프로그램 libmnl, libnl 사용 커널 공간 (Kernel Space) ioctl 핸들러 SIOCETHTOOL Netlink ethnl NETLINK_GENERIC rtnetlink RTNL_KIND_xxx ethtool 코어 (net/ethtool/) 요청 검증, ethtool_ops 디스패치, 기능 비트맵 관리 net_device dev->ethtool_ops ethtool_ops 콜백 테이블 get_drvinfo, get/set_link_ksettings, get/set_ringparam, get/set_coalesce, ... 하드웨어 (Hardware) NIC 레지스터 MMIO BAR, DMA 디스크립터 펌웨어/EEPROM NVM, SPI Flash PHY / SFP MDIO/I2C [그림 1] ethtool 서브시스템 전체 아키텍처 계층도

ioctl 경로 vs Netlink 경로 비교

항목ioctl (SIOCETHTOOL)Netlink (ethnl)
도입 시기 커널 2.4 (2001) 커널 5.6 (2020)
시스템 콜 ioctl(fd, SIOCETHTOOL, &ifr) sendmsg()/recvmsg() on Generic Netlink
데이터 직렬화 고정 크기 C 구조체 (copy_from_user) NLA(Netlink Attribute) TLV
확장성 낮음 (구조체 크기 고정) 높음 (새 속성 추가 용이)
비동기 알림 불가 가능 (multicast 그룹)
다중 요청 1 요청 = 1 ioctl 덤프 요청으로 전체 목록 조회
커널 소스 net/ethtool/ioctl.c net/ethtool/netlink.c 및 각 명령 파일
향후 전망 유지 보수 모드 (새 기능 미추가) 활발한 개발 중 (모든 새 기능)

ioctl 기반 ethtool 호출 흐름

기존 ioctl 기반 경로는 다음과 같은 순서로 동작합니다:

  1. 사용자 공간에서 ioctl(sock_fd, SIOCETHTOOL, &ifr)을 호출합니다. ifr.ifr_dataethtool_cmd 등의 구조체 포인터가 담깁니다.
  2. 커널의 dev_ioctl()SIOCETHTOOL을 인식하고 dev_ethtool()을 호출합니다.
  3. dev_ethtool()은 요청의 cmd 필드를 기준으로 적절한 처리 함수(ethtool_get_settings() 등)를 선택합니다.
  4. 처리 함수는 dev->ethtool_ops의 해당 콜백을 호출합니다.
  5. 드라이버 콜백이 하드웨어와 상호작용한 뒤 결과를 반환합니다.
  6. copy_to_user()로 결과를 사용자 공간에 전달합니다.
/* net/ethtool/ioctl.c - dev_ethtool() 핵심 디스패치 로직 (간략화) */
int dev_ethtool(struct net *net, struct ifreq *ifr,
               void __user *useraddr)
{
    struct net_device *dev;
    u32 ethcmd;
    int rc;

    /* 사용자 공간에서 명령 코드 복사 */
    if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
        return -EFAULT;

    dev = __dev_get_by_name(net, ifr->ifr_name);
    if (!dev)
        return -ENODEV;

    /* 권한 검사: 일부 명령은 CAP_NET_ADMIN 필요 */
    if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
        switch (ethcmd) {
        case ETHTOOL_GDRVINFO:
        case ETHTOOL_GSET:
        case ETHTOOL_GSTATS:
            break;  /* 읽기 전용은 허용 */
        default:
            return -EPERM;
        }
    }

    /* 명령별 디스패치 */
    switch (ethcmd) {
    case ETHTOOL_GSET:
        rc = ethtool_get_settings(dev, useraddr);
        break;
    case ETHTOOL_SSET:
        rc = ethtool_set_settings(dev, useraddr);
        break;
    case ETHTOOL_GDRVINFO:
        rc = ethtool_get_drvinfo(dev, useraddr);
        break;
    case ETHTOOL_GRINGPARAM:
        rc = ethtool_get_ringparam(dev, useraddr);
        break;
    /* ... 약 60개의 case ... */
    default:
        rc = -EOPNOTSUPP;
    }
    return rc;
}
코드 설명
  • 2-4행 dev_ethtool()은 네트워크 네임스페이스, ifreq, 사용자 포인터를 매개변수로 받습니다.
  • 9-10행 copy_from_user()로 사용자 공간에서 4바이트 명령 코드를 복사합니다. 모든 ethtool 구조체의 첫 필드는 __u32 cmd입니다.
  • 16-22행 CAP_NET_ADMIN 권한 검사. 일부 읽기 전용 명령은 일반 사용자도 실행 가능합니다.
  • 25-40행 switch 문으로 약 60개의 ethtool 명령을 각각의 처리 함수에 디스패치합니다.

커널 소스 파일 구조

ethtool 관련 커널 소스는 net/ethtool/ 디렉터리에 집중되어 있습니다:

파일역할
net/ethtool/ioctl.c기존 ioctl 기반 ethtool 처리 (dev_ethtool, ethtool_get_settings 등)
net/ethtool/netlink.cNetlink 기반 ethtool 공통 코드 (ethnl_ops 등록, 메시지 파싱)
net/ethtool/linkstate.cNetlink: 링크 상태 조회
net/ethtool/linkmodes.cNetlink: 링크 모드(속도/듀플렉스) 설정
net/ethtool/rings.cNetlink: 링 버퍼 매개변수
net/ethtool/coalesce.cNetlink: 코얼레싱 매개변수
net/ethtool/channels.cNetlink: 채널 매개변수
net/ethtool/features.cNetlink: 오프로드 기능 플래그
net/ethtool/stats.cNetlink: 표준 통계
net/ethtool/eee.cNetlink: EEE 매개변수
net/ethtool/fec.cNetlink: FEC 매개변수
net/ethtool/rss.cNetlink: RSS 매개변수
include/linux/ethtool.hethtool_ops 구조체 정의, 각종 매개변수 구조체
include/uapi/linux/ethtool.h사용자 공간 UAPI 헤더 (ethtool 명령 코드, 구조체)
include/uapi/linux/ethtool_netlink.hNetlink ethtool 속성 정의

ethtool_ops 구조체

구조체 개요

ethtool_ops는 NIC 드라이버가 ethtool 기능을 구현하기 위해 채워야 하는 콜백 함수 포인터 테이블입니다. include/linux/ethtool.h에 정의되어 있으며, net_deviceethtool_ops 멤버로 등록됩니다. 모든 콜백은 선택적이며, 구현하지 않으면 해당 ethtool 명령은 -EOPNOTSUPP를 반환합니다.

/* include/linux/ethtool.h - ethtool_ops 주요 콜백 (발췌) */
struct ethtool_ops {
    /* ---- 기본 정보 ---- */
    void (*get_drvinfo)(struct net_device *,
                        struct ethtool_drvinfo *);
    int  (*get_regs_len)(struct net_device *);
    void (*get_regs)(struct net_device *,
                     struct ethtool_regs *, void *);

    /* ---- 링크 설정 ---- */
    int  (*get_link_ksettings)(struct net_device *,
                              struct ethtool_link_ksettings *);
    int  (*set_link_ksettings)(struct net_device *,
                              const struct ethtool_link_ksettings *);

    /* ---- 링 버퍼 ---- */
    void (*get_ringparam)(struct net_device *,
                         struct ethtool_ringparam *,
                         struct kernel_ethtool_ringparam *,
                         struct netlink_ext_ack *);
    int  (*set_ringparam)(struct net_device *,
                         struct ethtool_ringparam *,
                         struct kernel_ethtool_ringparam *,
                         struct netlink_ext_ack *);

    /* ---- 코얼레싱 ---- */
    int  (*get_coalesce)(struct net_device *,
                        struct ethtool_coalesce *,
                        struct kernel_ethtool_coalesce *,
                        struct netlink_ext_ack *);
    int  (*set_coalesce)(struct net_device *,
                        struct ethtool_coalesce *,
                        struct kernel_ethtool_coalesce *,
                        struct netlink_ext_ack *);

    /* ---- 일시 중지 (Pause) ---- */
    void (*get_pauseparam)(struct net_device *,
                          struct ethtool_pauseparam *);
    int  (*set_pauseparam)(struct net_device *,
                          struct ethtool_pauseparam *);

    /* ---- 통계 ---- */
    void (*get_strings)(struct net_device *, u32, u8 *);
    void (*get_ethtool_stats)(struct net_device *,
                              struct ethtool_stats *, u64 *);
    int  (*get_sset_count)(struct net_device *, int);

    /* ---- 자기 진단 ---- */
    void (*self_test)(struct net_device *,
                      struct ethtool_test *, u64 *);

    /* ---- 채널 ---- */
    void (*get_channels)(struct net_device *,
                         struct ethtool_channels *);
    int  (*set_channels)(struct net_device *,
                         struct ethtool_channels *);

    /* ---- RSS ---- */
    u32  (*get_rxfh_key_size)(struct net_device *);
    u32  (*get_rxfh_indir_size)(struct net_device *);
    int  (*get_rxfh)(struct net_device *,
                     struct ethtool_rxfh_param *);
    int  (*set_rxfh)(struct net_device *,
                     struct ethtool_rxfh_param *,
                     struct netlink_ext_ack *);

    /* ---- 오프로드 기능 ---- */
    netdev_features_t (*fix_features)(struct net_device *,
                                     netdev_features_t);
    int  (*set_features)(struct net_device *,
                         netdev_features_t);

    /* ---- EEPROM ---- */
    int  (*get_eeprom_len)(struct net_device *);
    int  (*get_eeprom)(struct net_device *,
                       struct ethtool_eeprom *, u8 *);
    int  (*set_eeprom)(struct net_device *,
                       struct ethtool_eeprom *, u8 *);

    /* ---- EEE ---- */
    int  (*get_eee)(struct net_device *,
                    struct ethtool_keee *);
    int  (*set_eee)(struct net_device *,
                    struct ethtool_keee *);

    /* ---- N-tuple 필터 ---- */
    int  (*get_rxnfc)(struct net_device *,
                     struct ethtool_rxnfc *, u32 *);
    int  (*set_rxnfc)(struct net_device *,
                     struct ethtool_rxnfc *);

    /* ---- FEC ---- */
    int  (*get_fecparam)(struct net_device *,
                        struct ethtool_fecparam *);
    int  (*set_fecparam)(struct net_device *,
                        struct ethtool_fecparam *);

    /* ... 그 외 다수의 콜백 ... */
};
코드 설명
  • 3-8행 기본 정보 콜백. get_drvinfo()는 드라이버 이름/버전/버스 정보를 반환합니다. get_regs()는 NIC 레지스터를 원시 바이트로 덤프합니다.
  • 10-14행 링크 설정 콜백. link_ksettings는 비트맵 기반으로 25G/50G/100G/200G/400G까지 지원합니다. 기존 get/set_settings()는 더 이상 사용하지 않습니다.
  • 16-24행 링 버퍼 콜백. kernel_ethtool_ringparam은 커널 내부용 확장 매개변수, netlink_ext_ack는 Netlink 오류 메시지 전달용입니다.
  • 57-62행 RSS 콜백. get_rxfh_key_size()는 해시 키 크기, get_rxfh_indir_size()는 인디렉션 테이블 크기를 반환합니다. get/set_rxfh()는 실제 해시 키, 인디렉션 테이블, 해시 함수를 조작합니다.
  • 64-67행 오프로드 기능 콜백. fix_features()는 하드웨어 제약을 반영하여 기능 비트를 조정하고, set_features()는 실제 하드웨어 설정을 변경합니다.

ethtool_ops 등록 방법

드라이버는 netdev_set_ethtool_ops() 또는 SET_ETHTOOL_OPS() 매크로를 사용하여 net_device에 ethtool_ops를 등록합니다. 일반적으로 probe() 함수에서 네트워크 디바이스를 할당한 직후에 호출합니다.

/* 드라이버의 ethtool_ops 정적 정의 */
static const struct ethtool_ops my_ethtool_ops = {
    .get_drvinfo         = my_get_drvinfo,
    .get_link_ksettings  = my_get_link_ksettings,
    .set_link_ksettings  = my_set_link_ksettings,
    .get_ringparam       = my_get_ringparam,
    .set_ringparam       = my_set_ringparam,
    .get_coalesce        = my_get_coalesce,
    .set_coalesce        = my_set_coalesce,
    .get_channels        = my_get_channels,
    .set_channels        = my_set_channels,
    .get_strings         = my_get_strings,
    .get_ethtool_stats   = my_get_ethtool_stats,
    .get_sset_count      = my_get_sset_count,
    .get_link            = ethtool_op_get_link,  /* 커널 제공 헬퍼 */
    .get_rxfh_key_size   = my_get_rxfh_key_size,
    .get_rxfh_indir_size = my_get_rxfh_indir_size,
    .get_rxfh            = my_get_rxfh,
    .set_rxfh            = my_set_rxfh,
    .self_test           = my_self_test,
    .get_eeprom_len      = my_get_eeprom_len,
    .get_eeprom          = my_get_eeprom,
    .set_eeprom          = my_set_eeprom,
};

/* probe 함수에서 등록 */
static int my_nic_probe(struct pci_dev *pdev,
                        const struct pci_device_id *id)
{
    struct net_device *netdev;

    netdev = alloc_etherdev_mqs(sizeof(struct my_adapter),
                                 num_tx_queues, num_rx_queues);
    if (!netdev)
        return -ENOMEM;

    /* ethtool_ops 등록 */
    netdev->ethtool_ops = &my_ethtool_ops;

    /* ... 이후 register_netdev() 등 ... */
    return 0;
}

커널 제공 기본 헬퍼 함수

커널은 여러 공통 연산에 대해 기본 구현을 제공합니다. 드라이버는 이를 직접 사용하거나 자체 구현으로 대체할 수 있습니다:

헬퍼 함수용도내부 동작
ethtool_op_get_link() ethtool <dev> 링크 상태 netif_carrier_ok(dev) 반환
ethtool_op_get_ts_info() 타임스탬프 정보 소프트웨어 타임스탬프 기본값 반환
ethtool_convert_link_mode_to_legacy_u32() link_ksettings를 기존 형식으로 변환 비트맵 -> u32 변환

ethtool_link_ksettings 구조체

커널 4.6부터 도입된 ethtool_link_ksettings는 기존 ethtool_cmd를 대체하여 25G 이상의 고속 이더넷과 다양한 FEC 모드를 지원합니다. 핵심적인 차이점은 광고/지원 링크 모드를 __ETHTOOL_LINK_MODE_MASK_NBITS 크기의 비트맵으로 표현한다는 것입니다.

/* include/uapi/linux/ethtool.h */
struct ethtool_link_ksettings {
    struct ethtool_link_settings base;
    struct {
        DECLARE_BITMAP(supported,
                       __ETHTOOL_LINK_MODE_MASK_NBITS);
        DECLARE_BITMAP(advertising,
                       __ETHTOOL_LINK_MODE_MASK_NBITS);
        DECLARE_BITMAP(lp_advertising,
                       __ETHTOOL_LINK_MODE_MASK_NBITS);
    } link_modes;
};

struct ethtool_link_settings {
    __u32 cmd;              /* ETHTOOL_GLINKSETTINGS / ETHTOOL_SLINKSETTINGS */
    __u32 speed;            /* Mbps 단위 링크 속도 */
    __u8  duplex;           /* DUPLEX_HALF / DUPLEX_FULL / DUPLEX_UNKNOWN */
    __u8  port;             /* PORT_TP / PORT_FIBRE / PORT_DA 등 */
    __u8  phy_address;      /* PHY의 MDIO 주소 */
    __u8  autoneg;          /* AUTONEG_ENABLE / AUTONEG_DISABLE */
    __u8  mdio_support;     /* ETH_MDIO_SUPPORTS_C22 / C45 */
    __u32 eth_tp_mdix;      /* MDI-X 상태 */
    __u32 eth_tp_mdix_ctrl; /* MDI-X 제어 */
    __s8  link_mode_masks_nwords; /* 비트맵 워드 수 */
    __u8  transceiver;      /* XCVR_INTERNAL / XCVR_EXTERNAL */
    __u8  master_slave_cfg; /* 마스터/슬레이브 설정 */
    __u8  master_slave_state;/* 마스터/슬레이브 현재 상태 */
    __u32 lanes;            /* 물리 레인 수 */
};
ethtool -s eth0 speed 25000 autoneg on 커널 ethtool 코어 검증 + 디스패치 set_link_ksettings() 드라이버 콜백 PHY MDIO 레지스터 지원 링크 모드 비트맵 (ETHTOOL_LINK_MODE_xxx_BIT) 10M 10baseT_Half/Full 100M 100baseT_Half/Full 1G 1000baseT_Full 10G 10GbaseT/SR/LR 25G 25GbaseCR/SR 50G/100G 50GbaseCR/100GbaseSR4 200G/400G 200GbaseCR4/400GbaseSR8 FEC BaseR/RS/LLRS Autoneg Autoneg_BIT Pause Pause/Asym_Pause Backplane 1000baseKX/10GbaseKR 비트맵 크기: __ETHTOOL_LINK_MODE_MASK_NBITS (커널 6.x: 약 100+ 비트) 기존 ethtool_cmd의 u32 supported/advertising은 32비트 제한으로 25G+ 표현 불가 link_ksettings.link_modes.supported / advertising / lp_advertising 비트맵에 매핑 [그림 2] 링크 설정 흐름과 지원 모드 비트맵

드라이버 get_link_ksettings 구현 예제

static int my_get_link_ksettings(struct net_device *netdev,
                                  struct ethtool_link_ksettings *ks)
{
    struct my_adapter *adapter = netdev_priv(netdev);
    struct my_hw *hw = &adapter->hw;

    /* 지원 모드 설정 */
    ethtool_link_ksettings_zero_link_mode(ks, supported);
    ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full);
    ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full);
    ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseCR_Full);
    ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
    ethtool_link_ksettings_add_link_mode(ks, supported, Pause);

    /* 광고 모드: 현재 하드웨어 상태 반영 */
    ethtool_link_ksettings_zero_link_mode(ks, advertising);
    if (hw->autoneg) {
        ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
        if (hw->advertised_speed & MY_SPEED_25G)
            ethtool_link_ksettings_add_link_mode(ks, advertising, 25000baseCR_Full);
        if (hw->advertised_speed & MY_SPEED_10G)
            ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseT_Full);
    }

    /* 기본 설정 */
    ks->base.speed    = hw->link_speed;     /* Mbps */
    ks->base.duplex   = hw->link_duplex;     /* DUPLEX_FULL */
    ks->base.autoneg  = hw->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
    ks->base.port     = PORT_TP;

    return 0;
}

자동 협상(Autoneg) 동작 방식

자동 협상은 IEEE 802.3 표준에 정의된 프로토콜로, 양쪽 PHY가 지원하는 최고 속도와 듀플렉스 모드를 자동으로 선택합니다. ethtool에서 자동 협상을 제어하는 방법:

# 자동 협상 활성화, 광고 모드 지정
ethtool -s eth0 autoneg on advertise 0x80000000000

# 자동 협상 비활성화, 고정 속도/듀플렉스 설정
ethtool -s eth0 autoneg off speed 10000 duplex full

# 현재 링크 설정 확인
ethtool eth0
주의: 자동 협상을 비활성화하고 양쪽 끝의 설정이 일치하지 않으면 듀플렉스 불일치(duplex mismatch)가 발생하여 심각한 성능 저하나 연결 불안정을 초래합니다. 반드시 양쪽 끝을 동일하게 설정하세요.

FEC (Forward Error Correction)

FEC는 전송 데이터에 오류 정정 코드를 추가하여 수신 측에서 비트 오류를 복구하는 기술입니다. 25Gbps 이상의 고속 이더넷에서는 신호 감쇠와 노이즈로 인한 BER(Bit Error Rate)이 증가하므로, FEC가 안정적인 링크 유지에 필수적입니다.

FEC 모드표준오버헤드정정 능력지연주요 용도
None (Off) - 0% 없음 최소 1G/10G 짧은 DAC, 저BER 환경
BaseR (FC-FEC) IEEE 802.3 Clause 74 ~2.6% 약함 (BER ~1e-5 → 1e-12) ~50 ns 10G KR, 25G-CR/SR 짧은 거리
RS-FEC IEEE 802.3 Clause 91/108 ~2.7% 강함 (BER ~1e-4 → 1e-15) ~100 ns 25G/50G/100G 표준, 긴 거리
LLRS-FEC IEEE 802.3 Clause 161 ~1.3% 중간 ~30 ns 50G PAM4 저지연 환경

FEC 설정과 확인

# 현재 FEC 설정 확인
ethtool --show-fec eth0
# FEC parameters for eth0:
# Configured FEC encodings: Auto
# Active FEC encoding: RS

# FEC 모드 수동 설정
ethtool --set-fec eth0 encoding rs        # RS-FEC 강제
ethtool --set-fec eth0 encoding baser     # BaseR FEC 강제
ethtool --set-fec eth0 encoding off       # FEC 비활성화
ethtool --set-fec eth0 encoding auto      # 자동 협상 (권장)

# FEC 정정 카운터 확인 (드라이버 지원 시)
ethtool -S eth0 | grep -i fec
# 출력 예:
#   fec_corrected_blocks: 1523      (정정 성공한 블록 수)
#   fec_uncorrectable_blocks: 0     (정정 불가 블록 수 — 0이어야 정상)

# FEC 카운터 변화 모니터링
watch -d -n 1 'ethtool -S eth0 | grep -i fec'
FEC 문제 해결: fec_uncorrectable_blocks 카운터가 증가하면 BER이 FEC의 정정 능력을 초과한 것입니다. BaseR→RS-FEC로 전환하거나, 케이블/트랜시버를 교체하세요. fec_corrected_blocks가 빠르게 증가하는 것은 FEC가 정상 작동 중이지만 물리 계층 품질이 한계에 있음을 의미합니다.

링 버퍼 (Ring Buffer)

NIC 링 버퍼 개념

NIC 링 버퍼(Ring Buffer 또는 Descriptor Ring)는 NIC 하드웨어와 커널 드라이버 사이에서 패킷을 주고받기 위한 원형 큐(Circular Queue) 자료구조입니다. 각 엔트리(디스크립터)는 DMA 버퍼의 물리 주소, 길이, 상태 플래그 등을 포함합니다.

링 버퍼의 크기는 NIC가 처리할 수 있는 미처리 패킷(in-flight) 수를 결정하며, 성능과 메모리 사용량 사이의 균형을 조절하는 핵심 튜닝 포인트입니다.

RX 링 버퍼 (수신) desc 0 desc 1 desc 2 desc 3 HEAD desc 4 desc 5 NIC가 여기에 기록 TAIL 드라이버가 보충 처리 완료 (드라이버가 소비) NIC가 DMA로 기록 중 비어 있음 (DMA 버퍼 할당 대기) TX 링 버퍼 (송신) desc 0 desc 1 desc 2 HEAD desc 3 TAIL desc 4 NIC가 송신 후 HEAD 이동 드라이버가 새 패킷 추가 ethtool_ringparam 구조체 필드 매핑 rx_max_pending 최대 RX 디스크립터 수 rx_pending 현재 RX 디스크립터 수 tx_max_pending 최대 TX 디스크립터 수 tx_pending 현재 TX 디스크립터 수 rx_mini_pending / rx_jumbo_pending 미니/점보 프레임 전용 링 (선택적) [그림 3] RX/TX 링 버퍼 디스크립터 링 구조와 ethtool_ringparam 필드 매핑

링 버퍼 크기 튜닝

# 현재 링 버퍼 크기 확인
ethtool -g eth0
# Ring parameters of eth0:
# Pre-set maximums:
# RX:     4096
# TX:     4096
# Current hardware settings:
# RX:     512
# TX:     512

# 링 버퍼 크기 변경 (인터페이스 순간 다운/업 발생)
ethtool -G eth0 rx 2048 tx 2048
튜닝 지침:
  • RX 링 크기 증가: 패킷 드롭이 발생할 때(ethtool -S에서 rx_missed 또는 rx_dropped 증가). 버스트 트래픽을 흡수하는 버퍼 역할.
  • TX 링 크기 증가: 고처리량 환경에서 TX 큐가 꽉 차서 netif_stop_queue()가 빈번할 때.
  • 과도한 증가 주의: 링이 너무 크면 패킷이 링에 오래 머물러 지연(latency)이 증가합니다. 저지연 환경에서는 작은 링 + 코얼레싱 조정이 효과적입니다.
  • 메모리 영향: 디스크립터당 약 16~64바이트이지만, 각 RX 디스크립터에 연결된 DMA 버퍼(일반적으로 2KB~4KB)의 메모리 사용이 핵심입니다. 4096개 RX 디스크립터 x 4KB = 약 16MB.

드라이버 get/set_ringparam 구현 예제

static void my_get_ringparam(struct net_device *netdev,
                              struct ethtool_ringparam *ring,
                              struct kernel_ethtool_ringparam *kring,
                              struct netlink_ext_ack *extack)
{
    struct my_adapter *adapter = netdev_priv(netdev);

    ring->rx_max_pending = MY_MAX_RX_DESC;   /* 예: 4096 */
    ring->tx_max_pending = MY_MAX_TX_DESC;   /* 예: 4096 */
    ring->rx_pending     = adapter->rx_ring_count;
    ring->tx_pending     = adapter->tx_ring_count;
}

static int my_set_ringparam(struct net_device *netdev,
                             struct ethtool_ringparam *ring,
                             struct kernel_ethtool_ringparam *kring,
                             struct netlink_ext_ack *extack)
{
    struct my_adapter *adapter = netdev_priv(netdev);
    u32 new_rx = ring->rx_pending;
    u32 new_tx = ring->tx_pending;

    /* 범위 검증 */
    if (new_rx < MY_MIN_RX_DESC || new_rx > MY_MAX_RX_DESC)
        return -EINVAL;
    if (new_tx < MY_MIN_TX_DESC || new_tx > MY_MAX_TX_DESC)
        return -EINVAL;

    /* 2의 거듭제곱으로 정렬 (하드웨어 요구사항) */
    new_rx = roundup_pow_of_two(new_rx);
    new_tx = roundup_pow_of_two(new_tx);

    /* 변경사항이 없으면 조기 반환 */
    if (new_rx == adapter->rx_ring_count &&
        new_tx == adapter->tx_ring_count)
        return 0;

    adapter->rx_ring_count = new_rx;
    adapter->tx_ring_count = new_tx;

    /* 인터페이스가 열려 있으면 리셋 필요 */
    if (netif_running(netdev))
        return my_reinit_locked(adapter);

    return 0;
}

NUMA 인식 링 배치

NUMA 시스템에서 링 버퍼의 메모리 할당 위치는 성능에 큰 영향을 미칩니다. NIC가 연결된 PCIe 루트 컴플렉스와 동일한 NUMA 노드에 링 버퍼와 DMA 버퍼를 할당해야 NUMA 횡단(cross-node) 메모리 접근을 최소화할 수 있습니다.

링 크기 vs 메모리/지연 트레이드오프

RX 디스크립터 수DMA 버퍼 메모리 (2KB/desc)버스트 흡수최대 지연용도
128~256 KB낮음~50 µs저지연 트레이딩
256~512 KB보통~100 µs일반 서버
512~1 MB높음~200 µs기본값 (대부분)
1024~2 MB매우 높음~400 µs웹 서버, 프록시
2048~4 MB최대~800 µs대용량 파일 서버
4096~8 MB최대~1.6 ms벌크 전송 극대화

NUMA-aware 링 버퍼 튜닝 스크립트

#!/bin/bash
# NUMA 인식 링 버퍼 크기 튜닝 + 메모리 추정
NIC=${1:-eth0}
TARGET_RX=${2:-2048}
TARGET_TX=${3:-2048}

# NIC NUMA 노드 확인
NUMA=$(cat /sys/class/net/$NIC/device/numa_node 2>/dev/null)
echo "NIC: $NIC, NUMA node: ${NUMA:--1(none)}"

# 현재 링 크기 확인
echo "=== 현재 링 버퍼 ==="
ethtool -g $NIC | grep -A4 "Current"

# 채널 수 확인
CHANNELS=$(ethtool -l $NIC 2>/dev/null | grep "Combined:" | tail -1 | awk '{print $2}')
CHANNELS=${CHANNELS:-1}

# 메모리 추정 (디스크립터 64B + DMA 버퍼 2KB)
DESC_MEM=$((TARGET_RX * 64))
DMA_MEM=$((TARGET_RX * 2048))
TOTAL_PER_QUEUE=$(( (DESC_MEM + DMA_MEM) ))
TOTAL_MEM=$(( TOTAL_PER_QUEUE * CHANNELS * 2 ))  # RX + TX

echo "=== 메모리 추정 ==="
echo "  디스크립터/큐: $((DESC_MEM / 1024)) KB"
echo "  DMA 버퍼/큐: $((DMA_MEM / 1024)) KB"
echo "  총 (RX+TX, ${CHANNELS}채널): $((TOTAL_MEM / 1024 / 1024)) MB"

# 링 크기 변경
echo "=== 링 버퍼 변경: RX=$TARGET_RX TX=$TARGET_TX ==="
ethtool -G $NIC rx $TARGET_RX tx $TARGET_TX

# 변경 확인
echo "=== 변경 후 ==="
ethtool -g $NIC | grep -A4 "Current"

인터럽트 코얼레싱 (Interrupt Coalescing)

코얼레싱 원리

인터럽트 코얼레싱은 NIC가 패킷이 도착할 때마다 즉시 인터럽트를 발생시키는 대신, 일정 시간이 경과하거나 일정 수의 패킷이 누적된 후에 하나의 인터럽트를 발생시키는 기법입니다. 이를 통해 인터럽트 처리 오버헤드를 크게 줄이고 CPU 효율을 높입니다.

코얼레싱 없음 vs 코얼레싱 적용 비교 코얼레싱 없음 패킷 IRQ 8개 패킷 = 8회 IRQ 코얼레싱 적용 (rx-usecs=50, rx-frames=4) 패킷 IRQ 4개 누적 4개 누적 8개 패킷 = 2회 IRQ (75% IRQ 감소) rx-usecs 증가: 지연(latency) 증가, CPU 효율 향상 | rx-usecs 감소: 지연 감소, CPU 부하 증가 rx-frames 증가: 배치 크기 증가 | adaptive-rx on: 트래픽 패턴에 맞춰 자동 조정 [그림 4] 인터럽트 코얼레싱 타이밍 비교 다이어그램

ethtool_coalesce 구조체

/* include/uapi/linux/ethtool.h */
struct ethtool_coalesce {
    __u32 cmd;

    /* RX 코얼레싱 매개변수 */
    __u32 rx_coalesce_usecs;        /* 첫 패킷 후 IRQ까지 대기 시간 (마이크로초) */
    __u32 rx_max_coalesced_frames;  /* IRQ 전 최대 누적 패킷 수 */
    __u32 rx_coalesce_usecs_irq;    /* IRQ 처리 중일 때의 대기 시간 */
    __u32 rx_max_coalesced_frames_irq;

    /* TX 코얼레싱 매개변수 */
    __u32 tx_coalesce_usecs;
    __u32 tx_max_coalesced_frames;
    __u32 tx_coalesce_usecs_irq;
    __u32 tx_max_coalesced_frames_irq;

    /* 통계 코얼레싱 */
    __u32 stats_block_coalesce_usecs;

    /* 적응형 코얼레싱: 하드웨어가 트래픽에 맞춰 자동 조정 */
    __u32 use_adaptive_rx_coalesce;  /* 0=off, 1=on */
    __u32 use_adaptive_tx_coalesce;

    /* 패킷 속도 기반 코얼레싱 (적응형 대안) */
    __u32 pkt_rate_low;
    __u32 rx_coalesce_usecs_low;
    __u32 rx_max_coalesced_frames_low;
    __u32 tx_coalesce_usecs_low;
    __u32 tx_max_coalesced_frames_low;

    __u32 pkt_rate_high;
    __u32 rx_coalesce_usecs_high;
    __u32 rx_max_coalesced_frames_high;
    __u32 tx_coalesce_usecs_high;
    __u32 tx_max_coalesced_frames_high;

    __u32 rate_sample_interval;      /* 패킷 속도 측정 간격 (초) */
};

코얼레싱 사용 예시

# 현재 코얼레싱 설정 확인
ethtool -c eth0

# RX 코얼레싱: 50마이크로초 대기 또는 64개 패킷 누적 시 IRQ
ethtool -C eth0 rx-usecs 50 rx-frames 64

# TX 코얼레싱: 100마이크로초 대기 또는 128개 패킷 누적 시 IRQ
ethtool -C eth0 tx-usecs 100 tx-frames 128

# 적응형 코얼레싱 활성화 (NIC가 트래픽 패턴에 맞춰 자동 조정)
ethtool -C eth0 adaptive-rx on adaptive-tx on

# 저지연 설정 (거래 시스템, HFT 등)
ethtool -C eth0 rx-usecs 0 rx-frames 1 adaptive-rx off
적응형 코얼레싱: 많은 현대 NIC 드라이버(ixgbe, i40e, ice, mlx5 등)는 adaptive-rx on을 지원합니다. 드라이버가 초당 패킷 수(PPS)를 실시간으로 측정하여 코얼레싱 매개변수를 동적으로 조정합니다. 트래픽이 적을 때는 낮은 지연, 트래픽이 많을 때는 높은 처리량에 최적화됩니다.

ICE 코얼레싱 특수 설정

Intel E810(ice)은 HW 타이머 해상도가 4μs이며, rx-usecs-high로 Adaptive 모드의 상한을 바운딩할 수 있습니다. per-queue 코얼레싱도 지원합니다.

# ICE rx-usecs-high: Adaptive 모드 상한 바운딩 (0~236μs)
ethtool -C eth0 adaptive-rx on rx-usecs-high 100

# per-queue 코얼레싱: 큐 0,1에만 개별 설정
ethtool --per-queue eth0 queue_mask 0x3 --coalesce rx-usecs 32 tx-usecs 32

# per-queue 확인
ethtool --per-queue eth0 queue_mask 0x3 --show-coalesce
ICE 4μs 해상도: rx-usecs에 50을 설정하면 내부적으로 52μs(4×13)로 반올림됩니다. 정밀한 코얼레싱이 필요하면 4의 배수(8, 12, 16, 20, ...)로 설정하세요.

오프로드 기능 (Offload Features)

오프로드 기능 분류

ethtool을 통해 제어할 수 있는 오프로드 기능은 NIC 하드웨어가 CPU 대신 수행하는 네트워크 처리 작업입니다. 커널의 netdev_features_t 비트맵으로 관리됩니다.

기능ethtool 이름커널 플래그설명
TX 체크섬 tx-checksum-ipv4 NETIF_F_IP_CSUM IPv4 TCP/UDP 체크섬을 NIC가 계산
RX 체크섬 rx-checksum NETIF_F_RXCSUM 수신 패킷 체크섬을 NIC가 검증
TSO tx-tcp-segmentation NETIF_F_TSO 대형 TCP 세그먼트를 NIC가 분할
TSO6 tx-tcp6-segmentation NETIF_F_TSO6 IPv6 TCP 세그먼트 오프로드
GSO tx-generic-segmentation NETIF_F_GSO 소프트웨어 GSO (NIC 직전 분할)
GRO rx-gro NETIF_F_GRO 수신 패킷 병합 (소프트웨어)
GRO HW rx-gro-hw NETIF_F_GRO_HW 수신 패킷 병합 (하드웨어)
LRO rx-lro NETIF_F_LRO 대형 수신 오프로드 (라우팅 시 문제)
Scatter-Gather tx-scatter-gather NETIF_F_SG 비연속 메모리 DMA 전송
VLAN TX tx-vlan-hw-insert NETIF_F_HW_VLAN_CTAG_TX VLAN 태그를 NIC가 삽입
VLAN RX rx-vlan-hw-parse NETIF_F_HW_VLAN_CTAG_RX VLAN 태그를 NIC가 추출
ntuple 필터 rx-ntuple-filter NETIF_F_NTUPLE 하드웨어 기반 수신 필터링
수신 해싱 rx-hashing NETIF_F_RXHASH NIC가 패킷 해시값 제공 (RSS)

오프로드 기능 조회 및 제어

# 모든 오프로드 기능 조회
ethtool -k eth0

# 특정 기능 켜기/끄기
ethtool -K eth0 tso on gso on gro on
ethtool -K eth0 lro off
ethtool -K eth0 rx-checksum on tx-checksum-ip-generic on

# 단일 기능 끄기 (디버깅 용도)
ethtool -K eth0 gro off

netdev_features_t 비트맵 관리

커널 내부에서 오프로드 기능은 netdev_features_t 타입의 비트맵으로 관리됩니다. net_device 구조체에는 네 가지 기능 비트맵이 있습니다:

struct net_device {
    /* NIC 하드웨어가 지원하는 기능 (드라이버가 probe에서 설정) */
    netdev_features_t hw_features;

    /* 현재 활성화된 기능 */
    netdev_features_t features;

    /* 사용자가 변경할 수 없는 고정 기능 */
    netdev_features_t hw_enc_features;

    /* VLAN 디바이스에 상속되는 기능 */
    netdev_features_t vlan_features;
    /* ... */
};
LRO 주의: LRO(Large Receive Offload)는 패킷 헤더를 변경하므로 포워딩/라우팅/브리징 환경에서 사용하면 안 됩니다. 라우터/방화벽에서는 반드시 ethtool -K eth0 lro off로 비활성화하세요. GRO는 원본 패킷을 보존하므로 안전합니다.
Application send() 64KB SG Scatter-Gather Checksum IP/TCP 체크섬 HW? TSO NIC가 세그먼트 GSO 커널이 세그먼트 NIC TX DMA → 와이어 HW 지원 SW 폴백 RX 오프로드 파이프라인 NIC RX 와이어 → DMA RX Checksum NIC 검증 → UNNECESSARY GRO / GRO_HW 패킷 병합 → super-skb TCP/IP 스택 소켓 버퍼 전달 [그림 11] TX/RX 오프로드 파이프라인: App → SG → Checksum → TSO/GSO → NIC

오프로드 의존성 맵

일부 오프로드 기능은 다른 기능에 의존합니다. 예를 들어 TSO는 SG(Scatter-Gather)와 TX Checksum이 반드시 활성화되어야 작동합니다. 커널은 의존성을 자동으로 관리하여, TSO를 켜면 SG와 Checksum이 함께 켜집니다.

오프로드 기능의존하는 기능비활성화 시 영향
TSO (tx-tcp-segmentation) SG + TX Checksum SG/Checksum 끄면 TSO 자동 비활성
TSO6 (tx-tcp6-segmentation) SG + TX Checksum (IPv6) TSO와 동일한 의존 관계
GSO (tx-generic-segmentation) SG SG 끄면 GSO 자동 비활성
GRO (rx-gro) 없음 (독립) GRO 끄면 LRO로 폴백 가능 (비권장)
LRO (rx-lro) 없음 (독립) 포워딩 시 자동 비활성 (netdev_fix_features)
SG (tx-scatter-gather) 없음 (기반 기능) SG 끄면 TSO/GSO/UFO 모두 비활성
VXLAN TX offload hw_enc_features 설정 외부 헤더 체크섬 + 내부 TSO 동시 필요
GRO_HW (rx-gro-hw) GRO 활성 GRO 끄면 GRO_HW 비활성

TSO vs GSO 성능 비교

TSO(TCP Segmentation Offload)는 NIC 하드웨어가 64KB까지의 대형 TCP 세그먼트를 MTU 크기로 분할합니다. GSO(Generic Segmentation Offload)는 동일한 작업을 커널 소프트웨어가 수행하되, NIC 직전(dev_queue_xmit 시점)에서 분할하여 스택 내부의 처리 효율은 유지합니다.

오프로드 상호작용: 터널과 캡슐화

VXLAN, GENEVE, GRE 등 터널 프로토콜 사용 시 오프로드의 동작이 복잡해집니다. NIC가 터널 오프로드를 지원하면 hw_enc_features에 해당 비트가 설정되며, 외부/내부 헤더의 체크섬과 세그먼테이션을 동시에 처리할 수 있습니다.

# 오프로드 진단: 현재 상태 + 의존성 확인
ethtool -k eth0 | grep -E 'segmentation|scatter|checksum|generic'
# 출력 예:
# tx-checksum-ip-generic: on
# tx-tcp-segmentation: on
# tx-generic-segmentation: on [requested on]
# tx-scatter-gather: on

# 터널 오프로드 지원 확인
ethtool -k eth0 | grep -E 'tx-udp_tnl|tx-gre|encap'
# tx-udp_tnl-segmentation: on  → VXLAN/GENEVE TSO 지원
# tx-gre-segmentation: on      → GRE TSO 지원

# TSO on/off 성능 비교 (iperf3)
echo "=== TSO ON ==="
ethtool -K eth0 tso on
iperf3 -c 192.168.1.1 -t 10 -P 4

echo "=== TSO OFF (GSO 폴백) ==="
ethtool -K eth0 tso off
iperf3 -c 192.168.1.1 -t 10 -P 4

# 원복
ethtool -K eth0 tso on
흔한 오프로드 함정:
  • Linux bridge + TSO: 브리지 인터페이스에서 TSO가 켜져 있으면 일부 VM이 점보 패킷을 수신하여 통신 장애가 발생할 수 있습니다. 브리지 포트에서 ethtool -K br0 tso off gso off를 고려하세요.
  • tc mirred + checksum: tc mirred로 패킷을 다른 인터페이스로 리다이렉트할 때 체크섬 오프로드 상태가 전달되지 않아 잘못된 체크섬 패킷이 나갈 수 있습니다.
  • veth pair: veth 쌍에서 한쪽의 오프로드 설정이 다른쪽과 불일치하면 성능 저하나 패킷 드롭이 발생합니다. 양쪽 설정을 일치시키세요.
  • VLAN + Checksum: 일부 레거시 NIC는 VLAN 태그가 있을 때 체크섬 오프로드가 작동하지 않습니다. rx-checksum-bad 카운터를 모니터링하세요.

RSS (Receive Side Scaling)

RSS 개요

RSS(Receive Side Scaling)는 NIC가 수신 패킷의 해시값을 계산하여 여러 수신 큐에 분산하는 하드웨어 기능입니다. 각 수신 큐를 서로 다른 CPU에 바인딩하면 멀티코어 시스템에서 수신 처리를 병렬화할 수 있습니다.

수신 패킷 src/dst IP src/dst port protocol 해시 엔진 Toeplitz / XOR / CRC32 + 비밀 키 (40B) 인디렉션 테이블 (RETA, 128~512 엔트리) [0]: Q0 [1]: Q1 [2]: Q2 [3]: Q3 [4]: Q0 hash % table_size = index RX 큐 0 RX 큐 1 RX 큐 2 RX 큐 3 CPU 0 CPU 1 CPU 2 CPU 3 IRQ affinity로 큐-CPU 매핑 해시 입력: {src_IP, dst_IP, src_port, dst_port, protocol} (5-tuple) 같은 플로우의 패킷은 항상 같은 큐로 분산 -> 순서 보장, 캐시 지역성 극대화 ethtool -X eth0 equal 4 (균등 분산) | ethtool -X eth0 weight 1 2 1 2 (가중치) [그림 5] RSS(Receive Side Scaling) 패킷 분산 아키텍처

RSS 설정 예시

# 현재 RSS 인디렉션 테이블과 해시 키 확인
ethtool -x eth0

# 4개 큐에 균등 분산
ethtool -X eth0 equal 4

# 가중치 기반 분산 (큐0:1, 큐1:3, 큐2:1, 큐3:3)
ethtool -X eth0 weight 1 3 1 3

# 해시 함수 변경 (xor 또는 toeplitz)
ethtool -X eth0 hfunc toeplitz

# 해시 입력 필드 설정 (TCP4의 경우 4-tuple 사용)
ethtool -N eth0 rx-flow-hash tcp4 sdfn

# IRQ affinity 설정 (큐 N을 CPU N에 바인딩)
for i in 0 1 2 3; do
    echo $i > /proc/irq/$(cat /sys/class/net/eth0/queues/rx-$i/rps_cpus)/smp_affinity_list
done

해시 함수 비교

해시 함수특성용도
toeplitz Toeplitz 해시. 비밀 키(40바이트)와 결합하여 균등 분산. Microsoft RSS 표준 기본값. 대부분의 환경에 적합
xor 단순 XOR 연산. 계산 비용 최저 키 관리 불필요 환경
crc32 CRC32 해시. 일부 NIC에서 지원 특정 하드웨어 최적화

ICE RSS 해시 설정

Intel E810(ice)에서 RSS 해시 입력 필드를 프로토콜별로 세밀하게 제어할 수 있습니다.

# ICE rx-flow-hash: TCP4에 4-tuple (src/dst IP + src/dst port) 해시
ethtool -N eth0 rx-flow-hash tcp4 sdfn

# UDP4에 2-tuple (src/dst IP만) 해시 — 같은 포트의 다른 IP 분산
ethtool -N eth0 rx-flow-hash udp4 sd

# 현재 해시 설정 확인
ethtool -n eth0 rx-flow-hash tcp4
ethtool -n eth0 rx-flow-hash udp4

RFS와 N-tuple 필터

RFS(Receive Flow Steering)

RFS는 소프트웨어 기반의 수신 흐름 조향 메커니즘으로, 패킷을 처리하는 애플리케이션이 실행 중인 CPU로 해당 패킷을 전달합니다. RSS가 해시 기반으로 정적 분산한다면, RFS는 소켓-CPU 매핑 정보를 활용하여 동적으로 최적 CPU를 선택합니다.

# RFS 전역 해시 테이블 크기 설정 (일반적으로 32768 이상)
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

# 각 RX 큐별 RFS 플로우 수 설정
echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 4096 > /sys/class/net/eth0/queues/rx-1/rps_flow_cnt

N-tuple 필터 (Hardware Flow Director)

N-tuple 필터는 NIC 하드웨어에서 특정 패킷 패턴을 인식하여 지정된 수신 큐로 직접 전달하는 기능입니다. 소프트웨어 개입 없이 하드웨어 수준에서 패킷을 분류하므로 최소 지연으로 동작합니다.

# 현재 N-tuple 필터 규칙 조회
ethtool -n eth0

# 특정 포트의 TCP 패킷을 큐 3으로 전달
ethtool -N eth0 flow-type tcp4 dst-port 80 action 3

# 특정 IP 주소의 패킷을 큐 0으로 전달
ethtool -N eth0 flow-type tcp4 src-ip 192.168.1.100 action 0

# 5-tuple 완전 매칭 규칙
ethtool -N eth0 flow-type tcp4 \
    src-ip 10.0.0.1 dst-ip 10.0.0.2 \
    src-port 12345 dst-port 443 \
    action 2

# 규칙 삭제
ethtool -N eth0 delete 15

# 패킷 드롭 규칙 (action -1)
ethtool -N eth0 flow-type tcp4 dst-port 6666 action -1

ICE Flex Byte 필터 (user-def)

Intel E810(ice)은 N-tuple 필터에서 user-def 필드를 통해 Flex Byte 매칭을 지원합니다. 패킷 내 임의 오프셋의 바이트를 필터 조건으로 사용할 수 있습니다.

# ICE Flex Byte: user-def 상위 32비트=오프셋, 하위 32비트=매칭값
# 오프셋 0x3C(60바이트)에서 값 0xABCD 매칭 → 큐 7로 스티어링
ethtool -N eth0 flow-type tcp4 src-ip 10.0.0.1 \
    user-def 0x003C00000000ABCD action 7

# Flex Byte 필터 확인
ethtool -n eth0

RFS vs N-tuple vs RSS 비교

항목RSSRFSN-tuple
계층 하드웨어 (NIC) 소프트웨어 (커널) 하드웨어 (NIC)
분산 기준 해시 + 인디렉션 테이블 소켓-CPU 매핑 정확한 흐름 매칭
유연성 중간 (테이블 수정 가능) 높음 (자동 추적) 높음 (규칙 직접 지정)
지연 최소 (하드웨어) 낮음 (softIRQ) 최소 (하드웨어)
ethtool -x/-X sysfs 설정 -n/-N
조합 사용 RSS + RFS 조합 권장. N-tuple은 특정 고우선순위 플로우에 사용

N-tuple 규칙 우선순위

N-tuple 필터 규칙에는 location(위치) 파라미터가 있으며, 이 값이 규칙의 우선순위를 결정합니다. 낮은 location 번호가 더 높은 우선순위를 가집니다. 규칙이 충돌할 경우(동일한 패킷이 여러 규칙에 매칭), location이 가장 낮은 규칙이 적용됩니다.

flow-typesrc-ipdst-ipsrc-portdst-portVLAN/proto
tcp4OOOO-
udp4OOOO-
tcp6O (일부 NIC)O (일부 NIC)OO-
udp6O (일부 NIC)O (일부 NIC)OO-
ip4OO--proto
ether----VLAN/proto

N-tuple 고급 활용

# 위치(location) 지정 필터 — 낮은 번호 = 높은 우선순위
ethtool -N eth0 flow-type tcp4 dst-port 443 action 0 loc 0    # 최우선
ethtool -N eth0 flow-type tcp4 dst-port 80  action 1 loc 10   # 두 번째
ethtool -N eth0 flow-type tcp4 dst-port 8080 action 2 loc 20  # 세 번째

# 최대 규칙 수 확인
ethtool -n eth0 | head -3
# 출력 예: "Total 4 rules" / "256 RX rings available"

# VLAN 기반 필터링 (ether flow-type)
ethtool -N eth0 flow-type ether vlan 100 vlan-mask 0xf000 action 3

# 모든 N-tuple 규칙 일괄 삭제
for LOC in $(ethtool -n eth0 | grep "Filter:" | awk '{print $2}'); do
    ethtool -N eth0 delete $LOC
    echo "Deleted filter at location $LOC"
done

# 규칙 적중 통계 확인 (드라이버 지원 시)
ethtool -S eth0 | grep -i "fdir\|flow_director\|ntuple"

채널 (Channels)

채널 개요

ethtool에서 "채널"은 NIC의 하드웨어 큐(Queue)를 의미합니다. 각 채널은 독립적인 인터럽트 벡터, 링 버퍼 쌍(RX/TX), NAPI 인스턴스를 가집니다. 채널 수는 멀티코어 병렬 처리의 핵심 매개변수입니다.

/* include/uapi/linux/ethtool.h */
struct ethtool_channels {
    __u32 cmd;
    __u32 max_rx;       /* 최대 RX 전용 채널 수 */
    __u32 max_tx;       /* 최대 TX 전용 채널 수 */
    __u32 max_other;    /* 최대 기타 채널 수 (이벤트 큐 등) */
    __u32 max_combined;  /* 최대 결합(RX+TX) 채널 수 */
    __u32 rx_count;     /* 현재 RX 전용 채널 수 */
    __u32 tx_count;     /* 현재 TX 전용 채널 수 */
    __u32 other_count;  /* 현재 기타 채널 수 */
    __u32 combined_count; /* 현재 결합 채널 수 */
};

채널 설정 명령

# 현재 채널 수 확인
ethtool -l eth0
# Channel parameters for eth0:
# Pre-set maximums:
# RX:             0
# TX:             0
# Other:          1
# Combined:       64
# Current hardware settings:
# RX:             0
# TX:             0
# Other:          1
# Combined:       8

# 결합 채널 수를 16으로 변경 (인터페이스 리셋 발생)
ethtool -L eth0 combined 16

# RX/TX 분리 채널 (일부 NIC만 지원)
ethtool -L eth0 rx 8 tx 8
채널 수 결정 기준:
  • 일반적으로 combined 채널 수 = CPU 코어 수로 설정합니다.
  • 하이퍼스레딩 환경에서는 물리 코어 수로 설정하는 것이 캐시 효율에 유리합니다.
  • NUMA 시스템에서는 NIC가 연결된 NUMA 노드의 코어 수로 제한하면 NUMA 횡단 비용을 줄일 수 있습니다.
  • 채널을 늘리면 MSI-X 벡터도 그만큼 필요하므로, /proc/interrupts로 IRQ 할당을 확인하세요.

채널 유형과 구조

ethtool 채널에는 세 가지 유형이 있으며, 각각 다른 목적으로 MSI-X 벡터와 결합됩니다.

NIC 채널 (Queue) MSI-X 벡터 CPU 코어 Combined CH 0 RX Ring 0 + TX Ring 0 + NAPI Combined CH 1 RX Ring 1 + TX Ring 1 + NAPI Combined CH 2 RX Ring 2 + TX Ring 2 + NAPI Combined CH N-1 RX Ring N-1 + TX Ring N-1 Other CH (관리) 이벤트/펌웨어 알림 MSI-X Vector 0 MSI-X Vector 1 MSI-X Vector 2 MSI-X Vector N-1 MSI-X Vector N CPU 0 (NUMA 0) CPU 1 (NUMA 0) CPU 2 (NUMA 0) CPU N-1 (NUMA 0) smp_affinity 총 MSI-X 벡터 = combined_count + rx_count + tx_count + other_count [그림 8] 채널 → MSI-X 벡터 → CPU 코어 3단 바인딩 구조

채널 수 최적화

최적 채널 수는 NIC의 MSI-X 벡터 수, NUMA 토폴로지, HT(하이퍼스레딩) 여부를 종합적으로 고려하여 결정합니다. 기본 공식은 다음과 같습니다:

최적 채널 수 = min(물리 코어 수NUMA local, MSI-X 벡터 수 - 1)

드라이버최대 결합 채널최대 분리(RX/TX)Other기본값 결정 로직
iceCPU 수 또는 MSI-X-1 중 작은 값미지원 (combined만)1min(num_online_cpus(), msix_vectors-1)
mlx5최대 256지원 (RX/TX 독립)0NUMA 로컬 코어 수
bnxt_en최대 128지원1 (async)min(cpus, msix-1)
igb최대 8미지원0-1min(cpus, 8)
ixgbe최대 64지원 (RX/TX 독립)1 (flow director)코어 수 기반
virtio_net최대 가상 큐 수미지원1 (ctrl vq)호스트 설정 의존

NUMA-aware 채널 + IRQ affinity 설정

#!/bin/bash
# NUMA-aware 채널 설정 + IRQ affinity 자동 구성
NIC=${1:-eth0}

# NIC의 NUMA 노드 확인
NUMA_NODE=$(cat /sys/class/net/$NIC/device/numa_node)
echo "NIC NUMA node: $NUMA_NODE"

# 해당 NUMA 노드의 물리 코어 수 확인
NUMA_CPUS=$(lscpu -p=cpu,node | grep -v '#' | \
    awk -F, -v n="$NUMA_NODE" '$2==n {print $1}')
NUM_CPUS=$(echo "$NUMA_CPUS" | wc -l)
echo "NUMA local CPUs: $NUM_CPUS ($NUMA_CPUS)"

# 사용 가능한 MSI-X 벡터 수 확인
MSIX=$(grep "$NIC" /proc/interrupts | wc -l)
echo "Current MSI-X vectors: $MSIX"

# 최적 채널 수 계산
OPTIMAL=$(( NUM_CPUS < MSIX ? NUM_CPUS : MSIX ))
echo "Setting combined channels to: $OPTIMAL"

# 채널 수 설정
ethtool -L $NIC combined $OPTIMAL

# IRQ affinity를 NUMA 로컬 CPU로 고정
IDX=0
for IRQ in $(grep "$NIC-TxRx" /proc/interrupts | awk '{print $1}' | tr -d ':'); do
    CPU=$(echo "$NUMA_CPUS" | sed -n "$((IDX % NUM_CPUS + 1))p")
    echo $CPU > /proc/irq/$IRQ/smp_affinity_list
    echo "  IRQ $IRQ → CPU $CPU"
    IDX=$((IDX + 1))
done

echo "Done. Verify: cat /proc/interrupts | grep $NIC"
커널 6.x 동적 큐 API: 커널 6.3부터 netdev_rx_queue_restart() API가 도입되어 개별 RX 큐를 인터페이스 전체 리셋 없이 재구성할 수 있습니다. 기존에는 채널 수를 변경하면 ndo_stop() + ndo_open()으로 전체 인터페이스가 리셋되었지만, 이 API를 구현한 드라이버(현재 ice, bnxt)에서는 트래픽 중단 없이 큐별 설정 변경이 가능합니다.

통계 (Statistics)

ethtool 통계 체계

NIC 드라이버는 ethtool을 통해 하드웨어 통계 카운터를 사용자 공간에 노출합니다. 이 통계는 세 가지 콜백의 조합으로 구현됩니다:

/* 통계 항목 정의 */
enum my_stats {
    MY_STAT_RX_PACKETS,
    MY_STAT_TX_PACKETS,
    MY_STAT_RX_BYTES,
    MY_STAT_TX_BYTES,
    MY_STAT_RX_ERRORS,
    MY_STAT_TX_ERRORS,
    MY_STAT_RX_DROPPED,
    MY_STAT_RX_CRC_ERRORS,
    MY_STAT_RX_MISSED,
    MY_STAT_TX_TIMEOUT,
    MY_STAT_COUNT,
};

static const char my_stat_strings[][ETH_GSTRING_LEN] = {
    "rx_packets",
    "tx_packets",
    "rx_bytes",
    "tx_bytes",
    "rx_errors",
    "tx_errors",
    "rx_dropped",
    "rx_crc_errors",
    "rx_missed",
    "tx_timeout",
};

static int my_get_sset_count(struct net_device *netdev, int sset)
{
    switch (sset) {
    case ETH_SS_STATS:
        return MY_STAT_COUNT;
    case ETH_SS_TEST:
        return MY_TEST_COUNT;
    default:
        return -EOPNOTSUPP;
    }
}

static void my_get_strings(struct net_device *netdev,
                            u32 sset, u8 *data)
{
    if (sset == ETH_SS_STATS)
        memcpy(data, my_stat_strings, sizeof(my_stat_strings));
}

static void my_get_ethtool_stats(struct net_device *netdev,
                                  struct ethtool_stats *stats,
                                  u64 *data)
{
    struct my_adapter *adapter = netdev_priv(netdev);

    /* 하드웨어 카운터 읽기 (MMIO 또는 DMA 통계 블록) */
    my_update_stats(adapter);

    data[MY_STAT_RX_PACKETS]   = adapter->stats.rx_packets;
    data[MY_STAT_TX_PACKETS]   = adapter->stats.tx_packets;
    data[MY_STAT_RX_BYTES]     = adapter->stats.rx_bytes;
    data[MY_STAT_TX_BYTES]     = adapter->stats.tx_bytes;
    data[MY_STAT_RX_ERRORS]    = adapter->stats.rx_errors;
    data[MY_STAT_TX_ERRORS]    = adapter->stats.tx_errors;
    data[MY_STAT_RX_DROPPED]   = adapter->stats.rx_dropped;
    data[MY_STAT_RX_CRC_ERRORS] = adapter->stats.rx_crc_errors;
    data[MY_STAT_RX_MISSED]    = adapter->stats.rx_missed;
    data[MY_STAT_TX_TIMEOUT]   = adapter->stats.tx_timeout;
}

통계 조회 및 모니터링

# NIC 하드웨어 통계 전체 조회
ethtool -S eth0

# 특정 통계 추적 (1초 간격 변화량 모니터링)
watch -d -n 1 'ethtool -S eth0 | grep -E "rx_dropped|rx_missed|tx_timeout"'

# 큐별 통계 확인 (드라이버 지원 시)
ethtool -S eth0 | grep "queue_0"

# 표준 Netlink 통계 (커널 5.14+)
ethtool --json -S eth0 --groups rmon
핵심 모니터링 지표:
  • rx_missed_errors: NIC 하드웨어에서 링 버퍼 부족으로 드롭. 링 버퍼 크기 증가 필요.
  • rx_crc_errors: 물리 계층 오류. 케이블/트랜시버 문제 확인.
  • rx_over_errors: 소프트웨어 큐 오버플로. 코얼레싱 조정 또는 CPU 병목 해소 필요.
  • tx_timeout: TX 큐가 일정 시간 내에 비워지지 않음. 하드웨어 행(hang) 가능성.
  • rx_fifo_errors: NIC 내부 FIFO 오버런. 처리 지연으로 인한 패킷 손실.
HW 카운터 MMIO 레지스터 DMA 통계 블록 Shadow 카운터 adapter→stats (64비트) 주기적 갱신 (watchdog) get_ethtool_stats() shadow → u64 data[] 순서: get_strings 매핑 사용자 공간 ethtool -S eth0 이름: 값 출력 MMIO 읽기 복사 ioctl/NL per-CPU 카운터 (u64_stats_sync) 큐별 통계 → 합산 시 seqcount 보호 표준 통계 그룹 (5.14+) eth-phy / eth-mac / rmon 그룹 [그림 12] 통계 수집 흐름: HW 카운터 → Shadow → Callback → 사용자 공간

통계 카운터 심화

NIC 하드웨어 통계 카운터에는 32비트64비트의 두 가지 폭이 있습니다. 32비트 카운터(특히 바이트 카운터)는 고속 네트워크에서 수 초 만에 랩어라운드(wrap-around)할 수 있으므로, 드라이버는 주기적으로(보통 watchdog 타이머) HW 카운터를 읽어 64비트 shadow 카운터에 누적합니다.

커널 5.14부터 Netlink ethtool은 표준 통계 그룹(eth-phy, eth-mac, rmon)을 정의하여 드라이버 간 통계 이름의 일관성을 확보하기 시작했습니다.

카테고리필드 예시출처의미커널 버전
eth-mac FramesTransmittedOK MAC 카운터 에러 없이 전송 완료된 프레임 수 5.14+
eth-mac FrameCheckSequenceErrors MAC 카운터 FCS 오류가 감지된 수신 프레임 수 5.14+
eth-phy SymbolErrorDuringCarrier PHY 카운터 캐리어 중 심볼 오류 수 5.14+
eth-ctrl MACControlFramesTransmitted MAC 카운터 Pause/PFC 제어 프레임 전송 수 5.14+
rmon etherStatsUndersizePkts HW RMON 64바이트 미만 수신 프레임 수 5.14+
rmon etherStatsJabbers HW RMON 최대 크기 초과 + FCS 오류 프레임 수 5.14+
드라이버 rx_queue_0_packets per-queue SW 큐 0 수신 패킷 수 (드라이버 자체) -
드라이버 tx_busy TX 경로 SW TX 큐가 꽉 차서 중단된 횟수 -
드라이버 rx_alloc_fail RX 경로 SW RX 버퍼 할당 실패 횟수 -
드라이버 link_down_events 펌웨어 링크 다운 이벤트 누적 수 -

벤더별 확장 통계

주요 NIC 드라이버는 표준 통계 외에 드라이버 고유의 확장 통계를 제공합니다. 이 통계들은 성능 분석과 문제 진단에 매우 유용하지만, 드라이버마다 이름 규칙이 다릅니다.

통계 변화량 모니터링 스크립트

#!/bin/bash
# NIC 통계 변화량 모니터링 (1초 간격 diff)
NIC=${1:-eth0}
INTERVAL=${2:-1}

get_stats() {
    ethtool -S $NIC 2>/dev/null | grep -E '^\s+\S+:' | \
        sed 's/^[[:space:]]*//' | sort
}

echo "=== $NIC 통계 변화량 ($INTERVAL초 간격) ==="
echo "Ctrl+C로 중지"

PREV=$(get_stats)
while true; do
    sleep $INTERVAL
    CURR=$(get_stats)

    # diff 계산 (변화가 있는 항목만 출력)
    paste <(echo "$PREV") <(echo "$CURR") | \
    while IFS=$'\t' read -r prev_line curr_line; do
        name=$(echo "$prev_line" | cut -d: -f1)
        pval=$(echo "$prev_line" | cut -d: -f2 | tr -d ' ')
        cval=$(echo "$curr_line" | cut -d: -f2 | tr -d ' ')
        if [ "$pval" != "$cval" ] 2>/dev/null; then
            diff=$((cval - pval))
            printf "  %-40s %+d  (%s)\n" "$name:" "$diff" "$cval"
        fi
    done
    echo "---"
    PREV="$CURR"
done
32비트 카운터 래핑 주의: 10Gbps에서 바이트 카운터(32비트)는 약 3.4초만에 래핑됩니다 (232 ÷ 10Gbps ≈ 3.4s). 100Gbps에서는 0.34초입니다. 드라이버의 watchdog 타이머 주기가 이보다 길면 통계 손실이 발생합니다. 최신 드라이버(ice, mlx5 등)는 대부분 64비트 HW 카운터를 사용하지만, 레거시 NIC(e1000, 8139too 등)는 32비트 카운터에 의존할 수 있습니다.

자기 진단 (Self-Test)

NIC 자기 진단 개요

ethtool -t는 NIC 하드웨어의 자기 진단(Self-Test)을 수행합니다. 드라이버가 self_test() 콜백을 구현하면, NIC는 다양한 하드웨어 검증 테스트를 수행하고 각 테스트의 통과/실패 결과를 반환합니다.

# 온라인 테스트 (링크 유지하며 비파괴적 테스트)
ethtool -t eth0 online

# 오프라인 테스트 (링크 다운 후 전체 하드웨어 테스트)
ethtool -t eth0 offline

일반적인 자기 진단 항목

테스트유형검증 대상
레지스터 테스트 온라인 NIC 레지스터 읽기/쓰기 검증
EEPROM 체크섬 온라인 NVM/EEPROM 데이터 무결성
인터럽트 테스트 온라인 MSI/MSI-X 인터럽트 전달 검증
루프백 테스트 (MAC) 오프라인 내부 MAC 루프백으로 패킷 전송/수신
루프백 테스트 (PHY) 오프라인 PHY 루프백으로 물리 계층 검증
링크 테스트 온라인 링크 상태 확인 (carrier)

드라이버 self_test 구현 예제

enum my_test {
    MY_TEST_REG = 0,
    MY_TEST_EEPROM,
    MY_TEST_IRQ,
    MY_TEST_LOOPBACK,
    MY_TEST_LINK,
    MY_TEST_COUNT,
};

static const char my_test_strings[][ETH_GSTRING_LEN] = {
    "Register test  (online)",
    "EEPROM test    (online)",
    "Interrupt test (online)",
    "Loopback test  (offline)",
    "Link test      (online)",
};

static void my_self_test(struct net_device *netdev,
                          struct ethtool_test *test,
                          u64 *data)
{
    struct my_adapter *adapter = netdev_priv(netdev);
    bool online_only = !(test->flags & ETH_TEST_FL_OFFLINE);

    memset(data, 0, MY_TEST_COUNT * sizeof(u64));

    /* 온라인 테스트: 항상 실행 */
    data[MY_TEST_REG]    = my_reg_test(adapter);
    data[MY_TEST_EEPROM] = my_eeprom_test(adapter);
    data[MY_TEST_IRQ]    = my_irq_test(adapter);
    data[MY_TEST_LINK]   = !netif_carrier_ok(netdev);

    /* 오프라인 테스트: 요청 시에만 */
    if (!online_only) {
        /* 인터페이스 일시 중지 */
        netif_carrier_off(netdev);
        data[MY_TEST_LOOPBACK] = my_loopback_test(adapter);
        /* 인터페이스 복원 */
        my_reinit_locked(adapter);
    }

    /* 하나라도 실패하면 전체 실패 플래그 */
    for (int i = 0; i < MY_TEST_COUNT; i++) {
        if (data[i])
            test->flags |= ETH_TEST_FL_FAILED;
    }
}

테스트 유형 상세

NIC 자기 진단의 각 테스트는 하드웨어의 서로 다른 영역을 검증합니다. 온라인 테스트는 링크를 유지한 채 실행되며, 오프라인 테스트는 일시적으로 인터페이스를 중단하고 더 깊은 하드웨어 검증을 수행합니다.

ethtool -t online/offline 모드? online Register R/W 패턴 쓰기/읽기 비교 EEPROM 체크섬 NVM 무결성 검증 Interrupt 전달 MSI-X 전달 확인 offline 링크 다운 netif_carrier_off 온라인 테스트 위 3개 모두 실행 루프백 테스트 MAC/PHY loopback 결과 수집 (u64 data[]) 0=통과, 비0=실패 PASS 모든 data[i] == 0 FAIL ETH_TEST_FL_FAILED 설정 [그림 13] Self-Test 실행 흐름: Online/Offline 분기와 테스트 캐스케이드

루프백 테스트 상세

루프백 테스트는 NIC 내부에서 패킷을 송신하고 곧바로 수신하여 데이터 경로의 무결성을 검증합니다. MAC 루프백과 PHY 루프백의 두 가지 수준이 있으며, 각각 검증하는 범위가 다릅니다.

테스트 실패 해석 가이드

실패 테스트HW 원인 가능성SW 원인 가능성권장 조치
Register R/W NIC 실리콘 결함, PCIe 링크 오류 드라이버 버그, 리소스 경합 PCIe AER 로그 확인, 슬롯 재장착, 펌웨어 업데이트
EEPROM 체크섬 NVM 셀 열화, 불완전한 FW 업데이트 - 벤더 NVM 도구로 EEPROM 재프로그래밍
Interrupt MSI-X 테이블 손상, IOMMU 설정 오류 IRQ 할당 실패, 드라이버 초기화 오류 dmesg IRQ 메시지 확인, IOMMU 설정 점검
MAC 루프백 MAC 블록 결함, DMA 엔진 이상 메모리 할당 실패, 버퍼 관리 버그 NIC 교체 고려, dmesg DMA 오류 확인
PHY 루프백 PHY 칩 결함, SerDes 링크 오류 MDIO 통신 타이밍 문제 PHY 레지스터 덤프, 트랜시버 교체 시도
Link 케이블 불량, 상대편 포트 장애 Autoneg 불일치, 속도 설정 오류 케이블 교체, 양쪽 설정 일치 확인

자동 진단 스크립트

#!/bin/bash
# NIC 자기 진단 실행 + 결과 파싱 스크립트
NIC=${1:-eth0}
MODE=${2:-online}

echo "=== $NIC self-test ($MODE) ==="

# 테스트 실행 및 결과 캡처
RESULT=$(ethtool -t $NIC $MODE 2>&1)
EXIT_CODE=$?

echo "$RESULT"
echo "---"

# 실패 항목 파싱
FAILURES=$(echo "$RESULT" | grep -c "FAIL")
if [ "$FAILURES" -gt 0 ]; then
    echo "[ALERT] $FAILURES개 테스트 실패!"
    echo "$RESULT" | grep "FAIL"
    # syslog 기록
    logger -t nic-diag "$NIC: $FAILURES self-test failures detected"
else
    echo "[OK] 모든 테스트 통과"
fi

EEPROM / 모듈 정보

NIC EEPROM 접근

대부분의 NIC는 온보드 EEPROM(또는 SPI Flash)에 MAC 주소, 부팅 설정, 펌웨어 이미지, 기본 구성 매개변수를 저장합니다. ethtool을 통해 이 데이터를 읽거나 수정할 수 있습니다.

# EEPROM 전체 덤프 (16진수)
ethtool -e eth0

# EEPROM 특정 오프셋 읽기 (오프셋 0부터 64바이트)
ethtool -e eth0 offset 0 length 64

# EEPROM 값 수정 (매우 주의! 잘못된 쓰기는 NIC를 벽돌로 만들 수 있음)
ethtool -E eth0 magic 0x10D38086 offset 0x12 value 0xAB
경고: ethtool -E를 사용한 EEPROM 쓰기는 극도로 위험합니다. 잘못된 값을 쓰면 NIC가 영구적으로 작동 불능 상태가 될 수 있습니다. magic 값은 드라이버별로 다르며, 보통 vendor_id | (device_id << 16) 형식입니다. 반드시 백업 후 진행하세요.

SFP/QSFP 모듈 정보

광학 모듈(SFP, SFP+, QSFP28 등)은 I2C/MDIO를 통해 접근 가능한 EEPROM을 내장하고 있으며, SFF-8472/SFF-8636 표준에 따른 디지털 진단 모니터링(DDM/DOM) 데이터를 제공합니다.

# SFP 모듈 EEPROM 정보 (A0 페이지: 모듈 식별)
ethtool -m eth0

# SFP 모듈 상세 정보 (raw 덤프)
ethtool -m eth0 raw on hex on

# 모듈 DDM 정보 확인 예시 출력:
#   Identifier     : SFP/SFP+/SFP28
#   Connector      : LC
#   Transceiver    : 25GBASE-SR
#   Laser wavelength: 850nm
#   Module temperature: 32.5 degrees C
#   Module voltage    : 3.30 V
#   Laser bias current: 6.75 mA
#   TX power          : -1.20 dBm
#   RX power          : -2.50 dBm

EEPROM 구조와 내용

NIC의 온보드 NVM(Non-Volatile Memory)은 일반적으로 다음과 같은 레이아웃으로 구성됩니다. 드라이버마다 EEPROM 접근 시 magic 값이 다르므로, 쓰기 작업 전에 반드시 해당 드라이버의 소스 코드를 확인해야 합니다.

드라이버NICmagic 값형식
iceIntel E8100x10D38086device_id << 16 | vendor_id
ixgbeIntel 825990x10FB8086device_id << 16 | vendor_id
e1000eIntel 825740x10D38086device_id << 16 | vendor_id
igbIntel I3500x15218086device_id << 16 | vendor_id
bnxt_enBroadcom NetXtreme-E0x669914e4device_id << 16 | vendor_id
mlx5Mellanox ConnectX-EEPROM 직접 접근 미지원 (devlink 사용)

SFP/QSFP 진단 상세

광학 모듈의 EEPROM은 SFF-8472(SFP/SFP+) 또는 SFF-8636(QSFP28/QSFP56) 표준을 따릅니다. SFP는 I2C 주소 A0h(식별)와 A2h(DDM)의 2페이지 구조이고, QSFP는 단일 I2C 주소에 페이지(00h~03h) 선택 방식을 사용합니다.

SFP/SFP+ (SFF-8472) A0h (ID) 0x00: Identifier 0x03: Connector 0x0C: Bit Rate 0x14: Vendor Name 0x3C: Wavelength 256바이트 A2h (DDM) 0x00: Thresholds 0x60: Temperature 0x62: Voltage 0x64: Bias Current 0x66: TX/RX Power 256바이트 I2C QSFP28/56 (SFF-8636) Lower (00h) 0x00: Identifier/Status 0x16: Temperature 128바이트 (고정) Upper 00h 0x80: Vendor/Serial 0xC0: Wavelength 128바이트 (페이지 선택) Page 01h Application Adv. Page 02h User/Vendor EEPROM Page 03h DDM Thresholds 페이지 선택 SFF-8472 vs SFF-8636 비교 SFF-8472: I2C 주소 2개 (A0h+A2h), 총 512B SFF-8636: I2C 주소 1개, 페이지 기반, 확장 가능 DDM: 온도/전압/바이어스/TX파워/RX파워 실시간 모니터 Threshold: 각 DDM 필드의 상한/하한 경보/경고 레벨 [그림 9] SFP(A0h/A2h) vs QSFP(페이지 기반) I2C 레지스터 맵 비교

EEPROM 심화 진단

# EEPROM 헥스 덤프 + MAC 주소 확인 (오프셋은 NIC별로 상이)
ethtool -e eth0 offset 0 length 32 | xxd

# SFP RX 파워 실시간 모니터링 (DDM)
watch -d -n 2 'ethtool -m eth0 | grep -E "power|temperature|voltage"'

# SFP 모듈 raw 덤프 → 파일 저장 (장애 시 오프라인 분석용)
ethtool -m eth0 raw on > sfp_dump_$(date +%Y%m%d).bin

# QSFP28 모듈 특정 페이지 덤프 (커널 5.10+)
ethtool -m eth0 hex on offset 0 length 256
ethtool -m eth0 hex on offset 256 length 128   # Upper page 00h
펌웨어/EEPROM 업데이트 안전 절차:
  • 1단계 (백업): ethtool -e eth0 > eeprom_backup.hex로 전체 덤프를 반드시 백업합니다.
  • 2단계 (전원 보호): UPS가 연결된 환경에서만 진행합니다. 쓰기 중 전원 차단은 NIC 벽돌화의 주요 원인입니다.
  • 3단계 (공식 도구): 가능하면 ethtool -E 대신 벤더 공식 도구(Intel NVMUpdate, Mellanox MFT, Broadcom bnxtnvm)를 사용하세요.
  • 4단계 (검증): 업데이트 후 ethtool -i eth0로 펌웨어 버전을 확인하고, ethtool -t eth0 offline으로 하드웨어 무결성을 검증합니다.

EEE (Energy Efficient Ethernet)

EEE 개요

EEE(Energy Efficient Ethernet, IEEE 802.3az)는 링크가 유휴 상태일 때 PHY를 저전력 유휴(Low Power Idle, LPI) 모드로 전환하여 전력 소비를 줄이는 기술입니다. 데이터센터에서 수천 대의 서버 NIC를 운영할 때 상당한 전력 절감 효과를 제공합니다.

/* include/linux/ethtool.h */
struct ethtool_keee {
    DECLARE_BITMAP(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
    DECLARE_BITMAP(advertised, __ETHTOOL_LINK_MODE_MASK_NBITS);
    DECLARE_BITMAP(lp_advertised, __ETHTOOL_LINK_MODE_MASK_NBITS);
    u32 tx_lpi_timer;     /* LPI 진입 전 대기 시간 (마이크로초) */
    bool tx_lpi_enabled;  /* TX LPI 활성화 여부 */
    bool eee_active;      /* EEE가 현재 활성 상태인지 */
    bool eee_enabled;     /* EEE 기능 활성화 여부 */
};

EEE 설정 명령

# EEE 상태 확인
ethtool --show-eee eth0

# EEE 활성화
ethtool --set-eee eth0 eee on

# EEE 비활성화 (저지연 환경에서 권장)
ethtool --set-eee eth0 eee off

# TX LPI 타이머 조정 (마이크로초)
ethtool --set-eee eth0 eee on tx-lpi on tx-timer 100
EEE와 지연: EEE는 LPI 모드 진입/복귀 시 수 마이크로초의 추가 지연이 발생합니다. 저지연이 중요한 환경(금융 거래, 실시간 제어)에서는 EEE를 비활성화하는 것이 권장됩니다. 데이터센터에서 대규모 전력 절감이 필요한 경우에만 활성화를 고려하세요.

LPI 상태 전이 상세

EEE의 핵심은 LPI(Low Power Idle) 상태 머신입니다. PHY는 Active → Sleep Request → Quiet(LPI) → Refresh 사이클을 반복하며, 각 전환 구간에서 정해진 타이밍을 준수해야 링크 무결성을 유지합니다.

Active 정상 데이터 전송 Sleep Req LPI 신호 전송 (Tw) Quiet (LPI) 저전력 유휴 (Tq) Refresh 동기화 유지 (Tr) 유휴 감지 Tw 만료 Tq 만료 Tr 완료 (반복) 데이터 도착 → Wake (Ts) tx_lpi_timer = Active → Sleep 대기 [그림 10] EEE LPI 상태 전이 다이어그램 (Active ↔ Quiet 사이클)

속도별 LPI 전환 시간

속도Tw (Wake 시간)Ts (Sleep 시간)Tr (Refresh 시간)최대 절전율
100BASE-TX~30 µs~200 µs~300 µs~80%
1000BASE-T~16.5 µs~182 µs~16.5 µs~90%
10GBASE-T~4.48 µs~2.88 µs~1.28 µs~92%
25GBASE-CR~3.5 µs~2.5 µs~1.0 µs~93%

EEE 절전 효과 측정과 진단

# EEE 상태 및 LPI 카운터 확인
ethtool --show-eee eth0

# PHY 레벨 LPI 통계 확인 (드라이버 지원 시)
ethtool -S eth0 | grep -i lpi
# 출력 예:
#   tx_lpi_status    : 1
#   rx_lpi_status    : 1
#   tx_lpi_count     : 48523
#   tx_lpi_duration  : 1284756 (µs)

# EEE vs 비EEE 지연 비교 측정
# 1) EEE 활성 상태에서 ping 지연 측정
ethtool --set-eee eth0 eee on
sleep 2
ping -c 100 -i 0.5 192.168.1.1 | tail -1
# rtt min/avg/max = 0.180/0.250/1.500 ms (LPI 복귀 지연 포함)

# 2) EEE 비활성 상태에서 ping 지연 측정
ethtool --set-eee eth0 eee off
sleep 2
ping -c 100 -i 0.5 192.168.1.1 | tail -1
# rtt min/avg/max = 0.100/0.120/0.200 ms (일관적 지연)

# WoL과 EEE 상호작용 확인
ethtool -s eth0 wol g     # WoL 활성화
ethtool --show-eee eth0   # EEE 상태 재확인 (일부 NIC는 WoL 시 EEE 비활성)
가상화/클라우드 환경에서의 EEE: 가상 NIC(virtio-net, vmxnet3, ENA 등)는 물리 PHY가 없으므로 EEE가 의미가 없습니다. 클라우드 인스턴스에서 ethtool --show-eee가 "not supported"를 반환하는 것은 정상입니다. 베어메탈 서버에서도 지연 민감 워크로드(HFT, 실시간 미디어, DB 복제)는 EEE를 BIOS/펌웨어 레벨에서 비활성화하는 것이 권장됩니다. 일부 NIC는 OS 레벨 설정과 별개로 펌웨어가 EEE를 재활성화할 수 있으므로 NVM 설정도 확인하세요.

ethnl 아키텍처

커널 5.6부터 도입된 Netlink 기반 ethtool(ethnl)은 기존 ioctl의 한계를 극복하기 위한 새로운 사용자-커널 인터페이스입니다. Generic Netlink 패밀리 "ethtool"로 등록되며, NLA(Netlink Attribute) TLV 형식으로 데이터를 교환합니다.

사용자 공간 ethtool CLI libmnl 사용 모니터링 도구 multicast 구독 Generic Netlink 소켓 (AF_NETLINK + NETLINK_GENERIC) 커널 공간 ethnl 디스패처 ethnl_default_doit/dumpit rings.c RINGS_GET/SET coalesce.c COALESCE_GET/SET linkmodes.c LINKMODES_GET/SET stats.c STATS_GET ... 20+ 모듈 ethtool_ops 드라이버 콜백 변경 알림 (multicast) [그림 6] Netlink ethtool (ethnl) 메시지 흐름과 모듈 구조

Netlink ethtool 명령 체계

Netlink ethtool은 각 기능마다 GET/SET/NTF(Notification) 명령 세트를 정의합니다:

명령 그룹GETSETNTF커널 소스
링크 모드ETHTOOL_MSG_LINKMODES_GETETHTOOL_MSG_LINKMODES_SETETHTOOL_MSG_LINKMODES_NTFlinkmodes.c
링크 상태ETHTOOL_MSG_LINKSTATE_GET--linkstate.c
링 버퍼ETHTOOL_MSG_RINGS_GETETHTOOL_MSG_RINGS_SETETHTOOL_MSG_RINGS_NTFrings.c
코얼레싱ETHTOOL_MSG_COALESCE_GETETHTOOL_MSG_COALESCE_SETETHTOOL_MSG_COALESCE_NTFcoalesce.c
채널ETHTOOL_MSG_CHANNELS_GETETHTOOL_MSG_CHANNELS_SETETHTOOL_MSG_CHANNELS_NTFchannels.c
기능ETHTOOL_MSG_FEATURES_GETETHTOOL_MSG_FEATURES_SETETHTOOL_MSG_FEATURES_NTFfeatures.c
EEEETHTOOL_MSG_EEE_GETETHTOOL_MSG_EEE_SETETHTOOL_MSG_EEE_NTFeee.c
통계ETHTOOL_MSG_STATS_GET--stats.c
FECETHTOOL_MSG_FEC_GETETHTOOL_MSG_FEC_SETETHTOOL_MSG_FEC_NTFfec.c
RSSETHTOOL_MSG_RSS_GET--rss.c

Netlink ethtool JSON 출력

# Netlink 기반 JSON 출력 (ethtool 5.10+)
ethtool --json -s eth0

# 링크 모드 JSON 출력
ethtool --json eth0

# 코얼레싱 JSON 출력
ethtool --json -c eth0

# Netlink 모니터링 (실시간 변경 알림 수신)
ethtool --monitor

Netlink ethnl 핵심 구현 패턴

/* net/ethtool/rings.c - Netlink ethtool 모듈 구현 예시 (간략화) */

/* 요청 구조체 */
struct rings_req_info {
    struct ethnl_req_info base;
};

/* 응답 구조체 */
struct rings_reply_data {
    struct ethnl_reply_data base;
    struct ethtool_ringparam ringparam;
    struct kernel_ethtool_ringparam kernel_ringparam;
};

/* 데이터 준비 콜백 */
static int rings_prepare_data(
    const struct ethnl_req_info *req_base,
    struct ethnl_reply_data *reply_base,
    const struct genl_info *info)
{
    struct rings_reply_data *data =
        container_of(reply_base, struct rings_reply_data, base);
    struct net_device *dev = reply_base->dev;

    /* 드라이버 콜백 호출 */
    if (!dev->ethtool_ops->get_ringparam)
        return -EOPNOTSUPP;

    dev->ethtool_ops->get_ringparam(
        dev, &data->ringparam,
        &data->kernel_ringparam, info->extack);

    return 0;
}

/* 응답 데이터를 Netlink 메시지에 채우기 */
static int rings_fill_reply(
    struct sk_buff *skb,
    const struct ethnl_req_info *req_base,
    const struct ethnl_reply_data *reply_base)
{
    const struct rings_reply_data *data =
        container_of(reply_base, const struct rings_reply_data, base);

    /* NLA 속성으로 데이터 인코딩 */
    if (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX,
                   data->ringparam.rx_max_pending) ||
        nla_put_u32(skb, ETHTOOL_A_RINGS_RX,
                   data->ringparam.rx_pending) ||
        nla_put_u32(skb, ETHTOOL_A_RINGS_TX_MAX,
                   data->ringparam.tx_max_pending) ||
        nla_put_u32(skb, ETHTOOL_A_RINGS_TX,
                   data->ringparam.tx_pending))
        return -EMSGSIZE;

    return 0;
}

/* ethnl_request_ops 등록 */
const struct ethnl_request_ops ethnl_rings_request_ops = {
    .request_cmd    = ETHTOOL_MSG_RINGS_GET,
    .reply_cmd      = ETHTOOL_MSG_RINGS_GET_REPLY,
    .hdr_attr       = ETHTOOL_A_RINGS_HEADER,
    .req_info_size  = sizeof(struct rings_req_info),
    .reply_data_size = sizeof(struct rings_reply_data),
    .prepare_data   = rings_prepare_data,
    .reply_size     = rings_reply_size,
    .fill_reply     = rings_fill_reply,
};

드라이버 구현 가이드

최소 ethtool_ops 구현

모든 NIC 드라이버는 최소한 다음 콜백을 구현해야 합니다. 이 콜백들이 없으면 기본적인 NIC 정보 조회와 링크 상태 확인이 불가능합니다.

/* 최소 권장 ethtool_ops */
static const struct ethtool_ops my_minimal_ethtool_ops = {
    /* 필수: 드라이버 정보 */
    .get_drvinfo         = my_get_drvinfo,

    /* 필수: 링크 상태 */
    .get_link            = ethtool_op_get_link,

    /* 강력 권장: 링크 설정 */
    .get_link_ksettings  = my_get_link_ksettings,
    .set_link_ksettings  = my_set_link_ksettings,

    /* 강력 권장: 통계 */
    .get_strings         = my_get_strings,
    .get_ethtool_stats   = my_get_ethtool_stats,
    .get_sset_count      = my_get_sset_count,
};

get_drvinfo 구현

static void my_get_drvinfo(struct net_device *netdev,
                             struct ethtool_drvinfo *info)
{
    struct my_adapter *adapter = netdev_priv(netdev);
    struct pci_dev *pdev = adapter->pdev;

    strscpy(info->driver, "my_nic", sizeof(info->driver));
    strscpy(info->version, "1.0.0", sizeof(info->version));
    strscpy(info->bus_info, pci_name(pdev),
            sizeof(info->bus_info));

    /* 펌웨어 버전 (하드웨어에서 읽기) */
    snprintf(info->fw_version, sizeof(info->fw_version),
             "%d.%d.%d",
             adapter->fw_major,
             adapter->fw_minor,
             adapter->fw_patch);

    info->n_stats   = MY_STAT_COUNT;
    info->testinfo_len = MY_TEST_COUNT;
    info->regdump_len  = my_get_regs_len(netdev);
    info->eedump_len   = my_get_eeprom_len(netdev);
}

실제 드라이버 참고: 주요 NIC 드라이버의 ethtool_ops 크기

드라이버NIC구현 콜백 수커널 소스 파일
iceIntel E810 (100G)약 40+drivers/net/ethernet/intel/ice/ice_ethtool.c
mlx5Mellanox ConnectX-6 (100G/200G)약 35+drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
bnxt_enBroadcom NetXtreme-E (100G)약 45+drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
igbIntel I350 (1G)약 30+drivers/net/ethernet/intel/igb/igb_ethtool.c
ixgbeIntel 82599 (10G)약 35+drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
i40eIntel X710/XL710 (40G)약 38+drivers/net/ethernet/intel/i40e/i40e_ethtool.c
e1000eIntel 82574/82579 (1G)약 25+drivers/net/ethernet/intel/e1000e/ethtool.c
virtio_net가상 NIC (QEMU/KVM)약 12+drivers/net/virtio_net.c

선택적 콜백 가이드

ethtool_ops의 약 80개 이상의 콜백 중 어떤 것을 구현해야 하는지는 드라이버의 NIC 복잡도에 따라 결정됩니다. 아래 표는 콜백을 4단계(Tier)로 분류한 가이드입니다.

Tierethtool 명령필수 콜백Netlink 대응
1 (필수) ethtool -i get_drvinfo ETHTOOL_MSG_STRSET_GET
1 (필수) ethtool eth0 get_link, get_link_ksettings ETHTOOL_MSG_LINKSTATE_GET, ETHTOOL_MSG_LINKMODES_GET
2 (권장) ethtool -S get_sset_count, get_strings, get_ethtool_stats ETHTOOL_MSG_STATS_GET
2 (권장) ethtool -g/-G get_ringparam, set_ringparam ETHTOOL_MSG_RINGS_GET/SET
2 (권장) ethtool -c/-C get_coalesce, set_coalesce ETHTOOL_MSG_COALESCE_GET/SET
3 (일반) ethtool -l/-L get_channels, set_channels ETHTOOL_MSG_CHANNELS_GET/SET
3 (일반) ethtool -x/-X get_rxfh, set_rxfh ETHTOOL_MSG_RSS_GET
3 (일반) ethtool -t self_test -
4 (고급) ethtool --show-eee get_eee, set_eee ETHTOOL_MSG_EEE_GET/SET
4 (고급) ethtool --show-fec get_fecparam, set_fecparam ETHTOOL_MSG_FEC_GET/SET

최신 커널 ethtool_ops 등록 패턴

커널 5.17+ 이후 ethtool_ops 등록 시에는 코얼레싱 파라미터 비트마스크와 커널 전용 확장 구조체를 함께 선언하는 것이 권장됩니다.

/* 커널 5.17+ 권장 등록 패턴 */
static const struct ethtool_ops my_ethtool_ops = {
    /* 코얼레싱: 드라이버가 지원하는 파라미터 명시 (5.7+) */
    .supported_coalesce_params =
        ETHTOOL_COALESCE_USECS |
        ETHTOOL_COALESCE_MAX_FRAMES |
        ETHTOOL_COALESCE_USE_ADAPTIVE_RX,

    /* Tier 1: 필수 */
    .get_drvinfo           = my_get_drvinfo,
    .get_link              = ethtool_op_get_link,
    .get_link_ksettings    = my_get_link_ksettings,
    .set_link_ksettings    = my_set_link_ksettings,

    /* Tier 2: 권장 */
    .get_sset_count        = my_get_sset_count,
    .get_strings           = my_get_strings,
    .get_ethtool_stats     = my_get_ethtool_stats,
    .get_ringparam         = my_get_ringparam,
    .set_ringparam         = my_set_ringparam,
    .get_coalesce          = my_get_coalesce,
    .set_coalesce          = my_set_coalesce,

    /* Tier 3: 일반 */
    .get_channels          = my_get_channels,
    .set_channels          = my_set_channels,
    .get_rxfh_key_size     = my_get_rxfh_key_size,
    .get_rxfh_indir_size   = my_get_rxfh_indir_size,
    .get_rxfh              = my_get_rxfh,
    .set_rxfh              = my_set_rxfh,
    .self_test             = my_self_test,

    /* Tier 4: 고급 */
    .get_eee               = my_get_eee,
    .set_eee               = my_set_eee,
    .get_fecparam          = my_get_fecparam,
    .set_fecparam          = my_set_fecparam,
};

/* probe()에서 등록 */
netdev->ethtool_ops = &my_ethtool_ops;
kernel_ethtool_ringparam: 커널 5.17부터 get_ringparam/set_ringparam 콜백에 struct kernel_ethtool_ringparam 파라미터가 추가되었습니다. 이 구조체는 tcp_data_split, cqe_size 등 Netlink 전용 확장 필드를 포함합니다. 새 드라이버는 반드시 이 시그니처를 사용해야 합니다.

디버깅과 문제 해결

일반적인 네트워크 성능 문제와 ethtool 진단

ethtool 기반 네트워크 문제 진단 흐름 네트워크 성능 문제 발생 1. ethtool eth0 (링크 상태) 속도/듀플렉스/링크 확인 링크 다운? 케이블/SFP/PHY 확인 2. ethtool -S eth0 (통계) 에러 카운터 확인 rx_missed 높음? ethtool -G: 링 버퍼 증가 rx_crc_errors 높음? 물리 계층 문제: 케이블/SFP 3. ethtool -c eth0 (코얼레싱) 인터럽트 설정 확인 CPU 과부하? ethtool -C: 코얼레싱 증가 지연 높음? ethtool -C: 코얼레싱 감소 4. ethtool -k eth0 (오프로드) TSO/GSO/GRO 상태 확인 5. ethtool -l/-x (채널/RSS) 멀티큐 분산 상태 확인 불균등 분산? ethtool -X: RSS 재설정 6. ethtool -t eth0 (진단) 하드웨어 자기 진단 실행 [그림 7] ethtool 기반 네트워크 문제 진단 흐름도

종합 진단 스크립트 예시

#!/bin/bash
# ethtool 종합 진단 스크립트
DEV="eth0"

echo "===== 1. 드라이버/하드웨어 정보 ====="
ethtool -i $DEV

echo "===== 2. 링크 상태 ====="
ethtool $DEV

echo "===== 3. 링 버퍼 설정 ====="
ethtool -g $DEV

echo "===== 4. 코얼레싱 설정 ====="
ethtool -c $DEV

echo "===== 5. 오프로드 기능 ====="
ethtool -k $DEV

echo "===== 6. 채널 구성 ====="
ethtool -l $DEV

echo "===== 7. RSS 인디렉션 테이블 ====="
ethtool -x $DEV

echo "===== 8. 하드웨어 통계 (에러 관련) ====="
ethtool -S $DEV | grep -iE "error|drop|miss|timeout|crc|fifo"

echo "===== 9. EEE 상태 ====="
ethtool --show-eee $DEV

echo "===== 10. 일시 중지 프레임 설정 ====="
ethtool -a $DEV

echo "===== 11. IRQ 분포 확인 ====="
grep $DEV /proc/interrupts | awk '{print $1, $NF}'

흔한 문제와 해결 방법

증상진단 명령원인해결
패킷 드롭(RX) ethtool -S | grep rx_missed 링 버퍼 부족 ethtool -G eth0 rx 4096
CRC 오류 다수 ethtool -S | grep crc 케이블/SFP 불량 케이블 교체, ethtool -m으로 광 출력 확인
CPU 100% (si 높음) ethtool -c eth0 코얼레싱 부족 ethtool -C eth0 adaptive-rx on
지연 높음 ethtool -c eth0, --show-eee 코얼레싱 과다, EEE 활성 ethtool -C eth0 rx-usecs 0, EEE off
처리량 낮음 ethtool -k eth0 오프로드 비활성 ethtool -K eth0 tso on gro on
CPU 편중 처리 ethtool -l, -x 채널/RSS 미설정 ethtool -L eth0 combined N, RSS 재설정
듀플렉스 불일치 ethtool eth0 한쪽만 autoneg off 양쪽 모두 autoneg on 또는 동일 설정
TX 타임아웃 ethtool -S | grep tx_timeout NIC 하드웨어 행(hang) ethtool -t eth0 offline, 드라이버/펌웨어 업데이트

추가 디버깅 도구 연계

# dropwatch: 커널 패킷 드롭 위치 추적
dropwatch -l kas

# perf로 네트워크 softIRQ 프로파일링
perf top -e 'irq:softirq_entry' -s comm

# /proc/net/softnet_stat: per-CPU softIRQ 통계
# 열: processed, dropped, time_squeeze, ...
cat /proc/net/softnet_stat

# /proc/interrupts: IRQ 분포 확인
watch -d -n 1 'cat /proc/interrupts | grep eth0'

# bpftrace: ethtool ioctl 추적
bpftrace -e 'kprobe:dev_ethtool { printf("ethtool called: dev=%s\n", str(((struct ifreq *)arg1)->ifr_name)); }'

커널 설정

필수 커널 설정 옵션

# ethtool 핵심 (기본 활성화)
CONFIG_NET=y
CONFIG_INET=y

# Netlink ethtool (커널 5.6+)
CONFIG_ETHTOOL_NETLINK=y

# 네트워크 디바이스 지원
CONFIG_NETDEVICES=y
CONFIG_ETHERNET=y

# 특정 NIC 드라이버 예시
CONFIG_E1000E=m          # Intel 82574/82579
CONFIG_IGB=m             # Intel I350
CONFIG_IXGBE=m           # Intel 82599 10G
CONFIG_I40E=m            # Intel X710/XL710
CONFIG_ICE=m             # Intel E810 100G
CONFIG_MLX5_CORE=m       # Mellanox ConnectX
CONFIG_BNXT=m            # Broadcom NetXtreme-E
CONFIG_VIRTIO_NET=m      # 가상 NIC

# RSS/RFS 관련
CONFIG_RPS=y             # Receive Packet Steering
CONFIG_RFS_ACCEL=y       # 하드웨어 가속 RFS
CONFIG_XPS=y             # Transmit Packet Steering

# 오프로드 관련
CONFIG_NET_FOU=m         # Foo-over-UDP (터널 오프로드)
CONFIG_NET_FOU_IP_TUNNELS=y

# PHY/SFP 관련
CONFIG_PHYLIB=y          # PHY 라이브러리
CONFIG_SFP=m             # SFP cage 지원
CONFIG_MDIO_DEVICE=y     # MDIO 버스

# 디버깅
CONFIG_DEBUG_NET=y       # 네트워크 디버깅 (개발 시)
CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m

sysfs/procfs 관련 경로

경로설명
/sys/class/net/eth0/queues/per-queue 설정 (rps_cpus, rps_flow_cnt, xps_cpus)
/sys/class/net/eth0/device/PCI 디바이스 정보 (vendor, device, driver)
/sys/class/net/eth0/statistics/표준 네트워크 통계 (rtnl_link_stats64)
/proc/interruptsper-CPU IRQ 카운터
/proc/net/softnet_statper-CPU softIRQ 처리 통계
/proc/sys/net/core/netdev_budgetNAPI 폴링 버짓 (기본 300)
/proc/sys/net/core/rps_sock_flow_entriesRFS 전역 해시 테이블 크기

참고자료

다음 학습: