ethtool 서브시스템 심화
ethtool은 리눅스 네트워크 인터페이스의 하드웨어 수준 설정과 진단을 위한 핵심 도구이자 커널 서브시스템입니다.
사용자 공간의 ethtool 명령은 커널의 ethtool_ops 콜백 테이블을 통해
NIC 드라이버와 직접 상호작용합니다. 링크 속도/듀플렉스 설정, 링 버퍼 크기 조정, 인터럽트 코얼레싱,
오프로드 기능 제어, RSS(Receive Side Scaling), 채널 매핑, 하드웨어 통계, 자기 진단(Self-Test),
EEPROM 접근, EEE(Energy Efficient Ethernet), 그리고 최신 Netlink 기반 인터페이스까지
ethtool 서브시스템의 전체 아키텍처와 드라이버 구현 방법을 상세히 다룹니다.
핵심 요약
- ethtool_ops -- NIC 드라이버가 구현하는 콜백 함수 테이블. 약 80개 이상의 연산을 정의합니다.
- ioctl vs Netlink -- 기존 ioctl(SIOCETHTOOL) 방식에서 Netlink(NETLINK_GENERIC / ethnl) 방식으로 전환 중입니다.
- 링크 설정 -- 속도, 듀플렉스, 자동 협상, FEC 등 물리 계층 매개변수를 제어합니다.
- 성능 튜닝 -- 링 버퍼, 코얼레싱, RSS, 채널, 오프로드를 조합하여 최적 성능을 달성합니다.
- 진단/모니터링 -- 통계, 자기 진단, EEPROM, 레지스터 덤프로 하드웨어 문제를 파악합니다.
단계별 이해
- 아키텍처 파악
사용자 공간 ethtool 명령이 커널 ethtool_ops를 거쳐 드라이버에 도달하는 경로를 이해합니다. - ethtool_ops 구조체 분석
콜백 함수 테이블의 각 멤버가 어떤 ethtool 명령에 매핑되는지 파악합니다. - 링크/링 버퍼/코얼레싱 설정
가장 빈번하게 사용하는 3대 튜닝 항목의 내부 동작을 추적합니다. - Netlink 인터페이스 학습
새로운 Netlink 기반 ethtool API의 구조와 기존 ioctl과의 차이점을 확인합니다. - 드라이버 구현 실습
실제 드라이버에서 ethtool_ops를 등록하고 콜백을 구현하는 패턴을 익힙니다.
ethtool 개요
ethtool이란
ethtool은 리눅스 시스템에서 이더넷 네트워크 인터페이스 카드(NIC)의 하드웨어 수준 매개변수를 조회하고 설정하기 위한 표준 도구이자 커널 서브시스템입니다. 1999년 David S. Miller가 초기 프레임워크를 설계한 이래, 리눅스 네트워크 스택에서 가장 널리 사용되는 NIC 관리 인터페이스로 자리 잡았습니다.
ethtool 서브시스템은 크게 세 가지 계층으로 구성됩니다:
- 사용자 공간 도구:
ethtool명령줄 유틸리티./usr/sbin/ethtool에 위치합니다. - 커널 ethtool 코어:
net/ethtool/디렉터리. ioctl 처리, Netlink 처리, 공통 검증 로직을 담당합니다. - 드라이버 콜백: 각 NIC 드라이버가
ethtool_ops구조체를 통해 구현하는 하드웨어별 연산입니다.
주요 기능 카테고리
| 카테고리 | 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 드라이버의 콜백 함수를 호출합니다. 드라이버는 하드웨어 레지스터를 직접 조작하거나 펌웨어 명령을 실행하여 결과를 반환합니다.
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 기반 경로는 다음과 같은 순서로 동작합니다:
- 사용자 공간에서
ioctl(sock_fd, SIOCETHTOOL, &ifr)을 호출합니다.ifr.ifr_data에ethtool_cmd등의 구조체 포인터가 담깁니다. - 커널의
dev_ioctl()이SIOCETHTOOL을 인식하고dev_ethtool()을 호출합니다. dev_ethtool()은 요청의cmd필드를 기준으로 적절한 처리 함수(ethtool_get_settings()등)를 선택합니다.- 처리 함수는
dev->ethtool_ops의 해당 콜백을 호출합니다. - 드라이버 콜백이 하드웨어와 상호작용한 뒤 결과를 반환합니다.
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(ðcmd, 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.c | Netlink 기반 ethtool 공통 코드 (ethnl_ops 등록, 메시지 파싱) |
net/ethtool/linkstate.c | Netlink: 링크 상태 조회 |
net/ethtool/linkmodes.c | Netlink: 링크 모드(속도/듀플렉스) 설정 |
net/ethtool/rings.c | Netlink: 링 버퍼 매개변수 |
net/ethtool/coalesce.c | Netlink: 코얼레싱 매개변수 |
net/ethtool/channels.c | Netlink: 채널 매개변수 |
net/ethtool/features.c | Netlink: 오프로드 기능 플래그 |
net/ethtool/stats.c | Netlink: 표준 통계 |
net/ethtool/eee.c | Netlink: EEE 매개변수 |
net/ethtool/fec.c | Netlink: FEC 매개변수 |
net/ethtool/rss.c | Netlink: RSS 매개변수 |
include/linux/ethtool.h | ethtool_ops 구조체 정의, 각종 매개변수 구조체 |
include/uapi/linux/ethtool.h | 사용자 공간 UAPI 헤더 (ethtool 명령 코드, 구조체) |
include/uapi/linux/ethtool_netlink.h | Netlink ethtool 속성 정의 |
ethtool_ops 구조체
구조체 개요
ethtool_ops는 NIC 드라이버가 ethtool 기능을 구현하기 위해 채워야 하는
콜백 함수 포인터 테이블입니다. include/linux/ethtool.h에 정의되어 있으며,
net_device의 ethtool_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 변환 |
링크 설정 (Link Settings)
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; /* 물리 레인 수 */
};
드라이버 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
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_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) 수를 결정하며, 성능과 메모리 사용량 사이의 균형을 조절하는 핵심 튜닝 포인트입니다.
링 버퍼 크기 튜닝
# 현재 링 버퍼 크기 확인
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) 메모리 접근을 최소화할 수 있습니다.
- NUMA affinity 할당: 최신 드라이버(ice, mlx5)는
dev_to_node()를 사용하여 NIC의 NUMA 노드에dma_alloc_coherent()와napi_alloc_frag()를 자동 배치합니다. - 캐시라인 정렬: 디스크립터 배열은 64바이트(L1 캐시라인) 경계에 정렬됩니다.
dma_alloc_coherent()는 기본적으로 페이지 정렬을 보장합니다. - False sharing 방지: RX와 TX 링의 head/tail 인덱스가 같은 캐시라인에 있으면 양쪽이 경합합니다. 성능 지향 드라이버는 이를 별도 캐시라인에 배치합니다(
____cacheline_aligned_in_smp).
링 크기 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 효율을 높입니다.
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
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
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;
/* ... */
};
ethtool -K eth0 lro off로 비활성화하세요. GRO는 원본 패킷을 보존하므로 안전합니다.
오프로드 의존성 맵
일부 오프로드 기능은 다른 기능에 의존합니다. 예를 들어 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 시점)에서 분할하여 스택 내부의 처리 효율은 유지합니다.
- TSO 장점: CPU 사용률 최소화, 최대 throughput. 64KB 세그먼트를 하나의 DMA 트랜잭션으로 NIC에 전달합니다.
- GSO 장점: HW TSO를 지원하지 않는 NIC에서도 동작. tc/iptables 등 커널 내 패킷 처리 경로에서 올바르게 작동합니다.
- 측정:
iperf3 -c <server> -t 30으로 TSO on/off 각각의 throughput과 CPU 사용률을 비교합니다.
오프로드 상호작용: 터널과 캡슐화
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에 바인딩하면 멀티코어 시스템에서 수신 처리를 병렬화할 수 있습니다.
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 비교
| 항목 | RSS | RFS | N-tuple |
|---|---|---|---|
| 계층 | 하드웨어 (NIC) | 소프트웨어 (커널) | 하드웨어 (NIC) |
| 분산 기준 | 해시 + 인디렉션 테이블 | 소켓-CPU 매핑 | 정확한 흐름 매칭 |
| 유연성 | 중간 (테이블 수정 가능) | 높음 (자동 추적) | 높음 (규칙 직접 지정) |
| 지연 | 최소 (하드웨어) | 낮음 (softIRQ) | 최소 (하드웨어) |
| ethtool | -x/-X |
sysfs 설정 | -n/-N |
| 조합 사용 | RSS + RFS 조합 권장. N-tuple은 특정 고우선순위 플로우에 사용 | ||
N-tuple 규칙 우선순위
N-tuple 필터 규칙에는 location(위치) 파라미터가 있으며,
이 값이 규칙의 우선순위를 결정합니다. 낮은 location 번호가 더 높은 우선순위를 가집니다.
규칙이 충돌할 경우(동일한 패킷이 여러 규칙에 매칭), location이 가장 낮은 규칙이 적용됩니다.
| flow-type | src-ip | dst-ip | src-port | dst-port | VLAN/proto |
|---|---|---|---|---|---|
tcp4 | O | O | O | O | - |
udp4 | O | O | O | O | - |
tcp6 | O (일부 NIC) | O (일부 NIC) | O | O | - |
udp6 | O (일부 NIC) | O (일부 NIC) | O | O | - |
ip4 | O | O | - | - | 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 벡터와 결합됩니다.
- Combined (결합): 하나의 채널이 RX+TX를 모두 처리합니다. 대부분의 최신 NIC는 이 모드를 기본으로 사용하며, 하나의 MSI-X 벡터로 RX/TX 인터럽트를 모두 수신합니다.
- Split (RX/TX 분리): RX와 TX 각각에 독립적인 큐와 MSI-X 벡터를 할당합니다. 일부 NIC(e.g., ixgbe, i40e)에서 지원하며, TX 집중 워크로드에서 유리할 수 있습니다.
- Other: 관리/이벤트 전용 채널입니다. 펌웨어 이벤트, 링크 상태 변경 알림 등에 사용됩니다. ice 드라이버는 1개의 Other 채널을 기본 할당합니다.
채널 수 최적화
최적 채널 수는 NIC의 MSI-X 벡터 수, NUMA 토폴로지, HT(하이퍼스레딩) 여부를 종합적으로 고려하여 결정합니다. 기본 공식은 다음과 같습니다:
최적 채널 수 = min(물리 코어 수NUMA local, MSI-X 벡터 수 - 1)
| 드라이버 | 최대 결합 채널 | 최대 분리(RX/TX) | Other | 기본값 결정 로직 |
|---|---|---|---|---|
| ice | CPU 수 또는 MSI-X-1 중 작은 값 | 미지원 (combined만) | 1 | min(num_online_cpus(), msix_vectors-1) |
| mlx5 | 최대 256 | 지원 (RX/TX 독립) | 0 | NUMA 로컬 코어 수 |
| bnxt_en | 최대 128 | 지원 | 1 (async) | min(cpus, msix-1) |
| igb | 최대 8 | 미지원 | 0-1 | min(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"
netdev_rx_queue_restart() API가 도입되어 개별 RX 큐를
인터페이스 전체 리셋 없이 재구성할 수 있습니다. 기존에는 채널 수를 변경하면
ndo_stop() + ndo_open()으로 전체 인터페이스가 리셋되었지만,
이 API를 구현한 드라이버(현재 ice, bnxt)에서는 트래픽 중단 없이 큐별 설정 변경이 가능합니다.
통계 (Statistics)
ethtool 통계 체계
NIC 드라이버는 ethtool을 통해 하드웨어 통계 카운터를 사용자 공간에 노출합니다. 이 통계는 세 가지 콜백의 조합으로 구현됩니다:
get_sset_count(ETH_SS_STATS): 통계 항목 개수 반환get_strings(ETH_SS_STATS): 각 통계 항목의 이름 문자열 반환get_ethtool_stats(): 각 통계 항목의 64비트 값 배열 반환
/* 통계 항목 정의 */
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 오버런. 처리 지연으로 인한 패킷 손실.
통계 카운터 심화
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 드라이버는 표준 통계 외에 드라이버 고유의 확장 통계를 제공합니다. 이 통계들은 성능 분석과 문제 진단에 매우 유용하지만, 드라이버마다 이름 규칙이 다릅니다.
- ice (Intel E810):
port.rx_broadcast,port.tx_size_1023등port.접두사.rx_q*_bytes,tx_q*_packets형태의 큐별 통계. - mlx5 (Mellanox ConnectX):
rx_vport_unicast_bytes,tx_pci_signal_integrity등vport/pci접두사.rx[N]_bytes형태의 큐별 통계. - bnxt_en (Broadcom):
rx_total_discard_pkts,tx_total_coal_pkts등_total_포함.rx_bytes_pri[N]형태의 우선순위별 통계.
통계 변화량 모니터링 스크립트
#!/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
자기 진단 (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 자기 진단의 각 테스트는 하드웨어의 서로 다른 영역을 검증합니다. 온라인 테스트는 링크를 유지한 채 실행되며, 오프라인 테스트는 일시적으로 인터페이스를 중단하고 더 깊은 하드웨어 검증을 수행합니다.
루프백 테스트 상세
루프백 테스트는 NIC 내부에서 패킷을 송신하고 곧바로 수신하여 데이터 경로의 무결성을 검증합니다. MAC 루프백과 PHY 루프백의 두 가지 수준이 있으며, 각각 검증하는 범위가 다릅니다.
- MAC 루프백: TX MAC → 내부 스위치 → RX MAC 경로만 검증. PHY를 거치지 않으므로 케이블 문제와 무관합니다.
- PHY 루프백: TX MAC → PHY TX → (내부 루프) → PHY RX → RX MAC 전체 경로 검증. 아날로그 프론트엔드를 포함하여 더 포괄적입니다.
테스트 실패 해석 가이드
| 실패 테스트 | 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 값이 다르므로,
쓰기 작업 전에 반드시 해당 드라이버의 소스 코드를 확인해야 합니다.
| 드라이버 | NIC | magic 값 | 형식 |
|---|---|---|---|
| ice | Intel E810 | 0x10D38086 | device_id << 16 | vendor_id |
| ixgbe | Intel 82599 | 0x10FB8086 | device_id << 16 | vendor_id |
| e1000e | Intel 82574 | 0x10D38086 | device_id << 16 | vendor_id |
| igb | Intel I350 | 0x15218086 | device_id << 16 | vendor_id |
| bnxt_en | Broadcom NetXtreme-E | 0x669914e4 | device_id << 16 | vendor_id |
| mlx5 | Mellanox ConnectX | - | EEPROM 직접 접근 미지원 (devlink 사용) |
SFP/QSFP 진단 상세
광학 모듈의 EEPROM은 SFF-8472(SFP/SFP+) 또는 SFF-8636(QSFP28/QSFP56) 표준을 따릅니다. SFP는 I2C 주소 A0h(식별)와 A2h(DDM)의 2페이지 구조이고, QSFP는 단일 I2C 주소에 페이지(00h~03h) 선택 방식을 사용합니다.
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
- 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
LPI 상태 전이 상세
EEE의 핵심은 LPI(Low Power Idle) 상태 머신입니다. PHY는 Active → Sleep Request → Quiet(LPI) → Refresh 사이클을 반복하며, 각 전환 구간에서 정해진 타이밍을 준수해야 링크 무결성을 유지합니다.
속도별 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 비활성)
ethtool --show-eee가 "not supported"를 반환하는 것은
정상입니다. 베어메탈 서버에서도 지연 민감 워크로드(HFT, 실시간 미디어, DB 복제)는
EEE를 BIOS/펌웨어 레벨에서 비활성화하는 것이 권장됩니다. 일부 NIC는 OS 레벨 설정과
별개로 펌웨어가 EEE를 재활성화할 수 있으므로 NVM 설정도 확인하세요.
Netlink ethtool 인터페이스
ethnl 아키텍처
커널 5.6부터 도입된 Netlink 기반 ethtool(ethnl)은 기존 ioctl의 한계를 극복하기 위한
새로운 사용자-커널 인터페이스입니다. Generic Netlink 패밀리 "ethtool"로
등록되며, NLA(Netlink Attribute) TLV 형식으로 데이터를 교환합니다.
Netlink ethtool 명령 체계
Netlink ethtool은 각 기능마다 GET/SET/NTF(Notification) 명령 세트를 정의합니다:
| 명령 그룹 | GET | SET | NTF | 커널 소스 |
|---|---|---|---|---|
| 링크 모드 | ETHTOOL_MSG_LINKMODES_GET | ETHTOOL_MSG_LINKMODES_SET | ETHTOOL_MSG_LINKMODES_NTF | linkmodes.c |
| 링크 상태 | ETHTOOL_MSG_LINKSTATE_GET | - | - | linkstate.c |
| 링 버퍼 | ETHTOOL_MSG_RINGS_GET | ETHTOOL_MSG_RINGS_SET | ETHTOOL_MSG_RINGS_NTF | rings.c |
| 코얼레싱 | ETHTOOL_MSG_COALESCE_GET | ETHTOOL_MSG_COALESCE_SET | ETHTOOL_MSG_COALESCE_NTF | coalesce.c |
| 채널 | ETHTOOL_MSG_CHANNELS_GET | ETHTOOL_MSG_CHANNELS_SET | ETHTOOL_MSG_CHANNELS_NTF | channels.c |
| 기능 | ETHTOOL_MSG_FEATURES_GET | ETHTOOL_MSG_FEATURES_SET | ETHTOOL_MSG_FEATURES_NTF | features.c |
| EEE | ETHTOOL_MSG_EEE_GET | ETHTOOL_MSG_EEE_SET | ETHTOOL_MSG_EEE_NTF | eee.c |
| 통계 | ETHTOOL_MSG_STATS_GET | - | - | stats.c |
| FEC | ETHTOOL_MSG_FEC_GET | ETHTOOL_MSG_FEC_SET | ETHTOOL_MSG_FEC_NTF | fec.c |
| RSS | ETHTOOL_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 | 구현 콜백 수 | 커널 소스 파일 |
|---|---|---|---|
| ice | Intel E810 (100G) | 약 40+ | drivers/net/ethernet/intel/ice/ice_ethtool.c |
| mlx5 | Mellanox ConnectX-6 (100G/200G) | 약 35+ | drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c |
| bnxt_en | Broadcom NetXtreme-E (100G) | 약 45+ | drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c |
| igb | Intel I350 (1G) | 약 30+ | drivers/net/ethernet/intel/igb/igb_ethtool.c |
| ixgbe | Intel 82599 (10G) | 약 35+ | drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c |
| i40e | Intel X710/XL710 (40G) | 약 38+ | drivers/net/ethernet/intel/i40e/i40e_ethtool.c |
| e1000e | Intel 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)로 분류한 가이드입니다.
| Tier | ethtool 명령 | 필수 콜백 | 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;
get_ringparam/set_ringparam
콜백에 struct kernel_ethtool_ringparam 파라미터가 추가되었습니다.
이 구조체는 tcp_data_split, cqe_size 등 Netlink 전용 확장 필드를 포함합니다.
새 드라이버는 반드시 이 시그니처를 사용해야 합니다.
디버깅과 문제 해결
일반적인 네트워크 성능 문제와 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/interrupts | per-CPU IRQ 카운터 |
/proc/net/softnet_stat | per-CPU softIRQ 처리 통계 |
/proc/sys/net/core/netdev_budget | NAPI 폴링 버짓 (기본 300) |
/proc/sys/net/core/rps_sock_flow_entries | RFS 전역 해시 테이블 크기 |
참고자료
- 커널 공식 문서: Netlink interface for ethtool
- 커널 공식 문서: Scaling in the Linux Networking Stack (RSS/RPS/RFS/XPS)
- ethtool 사용자 공간 도구 공식 Git 저장소
- ethtool(8) 매뉴얼 페이지
- 커널 소스: include/linux/ethtool.h (ethtool_ops 정의)
- 커널 소스: net/ethtool/ 디렉터리
- 커널 소스: include/uapi/linux/ethtool.h (UAPI 헤더)
- 커널 공식 문서: NAPI
- LWN.net: The first satisfying ethtool netlink patches (2019)
- IEEE 802.3az: Energy Efficient Ethernet 표준
- Network Device 드라이버(net_device) -- ethtool_ops의 상위 프레임워크인 net_device 구조를 이해합니다.
- NAPI (New API) -- 코얼레싱과 밀접한 인터럽트 완화 메커니즘을 학습합니다.
- GSO/GRO 네트워크 오프로드 -- ethtool -K로 제어하는 오프로드 기능의 내부 동작을 파악합니다.
- 네트워크 스택 고급 -- RSS/RFS가 동작하는 네트워크 스택 전체 구조를 이해합니다.
- BPF/eBPF/XDP -- ethtool 채널과 연계되는 XDP 프로그램 부착을 학습합니다.