devlink 서브시스템 심화
devlink은 NIC/스위치 ASIC의 장치 단위 제어면을 제공하는 커널 인프라입니다. netdev 하나가 아닌 장치 전체를 대상으로 포트 분할, 리소스, trap, health, 펌웨어 갱신을 제어할 수 있습니다. 인스턴스 생명주기, 포트 타입(PF/VF/SF), subfunction 관리, devlink-param/region/resource/rate/linecard/sb, trap 그룹과 policer, health reporter 상태 머신, flash update, reload, 드라이버 구현 패턴(mlx5/ice)까지 devlink의 모든 것을 다룹니다.
핵심 요약
- devlink device -- 물리 장치(PCI 함수/ASIC) 단위의 제어 대상
- devlink port -- 물리 포트, PF/VF, CPU 포트 등의 논리 표현
- resource -- TCAM, FDB, ACL 같은 하드웨어 자원 관리
- health reporter -- 오류 탐지와 자동 복구 트리거
- trap -- 드롭/예외 패킷 이벤트를 제어면으로 수집
단계별 이해
- 장치 등록
드라이버가devlink_alloc(),devlink_register()를 호출합니다. - 포트/파라미터 노출
포트 타입과 튜닝 파라미터를 Netlink로 공개합니다. - 운영 중 모니터링
trap/health 카운터를 감시하고 이상 시 자동 복구를 시도합니다. - 유지보수 작업
reload/flash로 펌웨어와 런타임 구성을 업데이트합니다.
devlink 아키텍처
유저 공간의 devlink 유틸리티는 Generic Netlink를 통해 커널 net/devlink/ 코어와 통신합니다. 드라이버는 struct devlink_ops 콜백으로 기능을 제공합니다. devlink 코어는 2015년 커널 4.6에서 도입되었고, 이후 버전마다 trap, health, rate, linecard 등의 기능이 계속 추가되고 있습니다.
devlink은 기존 netdev 중심 제어와 달리, 물리 장치(PCI function/ASIC) 전체를 하나의 관리 단위로 묶습니다. 이를 통해 다음과 같은 장치 단위 작업이 가능합니다:
| 영역 | 기존 방식 | devlink 방식 | 이점 |
|---|---|---|---|
| 포트 관리 | ifconfig/ip link 개별 제어 | devlink port show/split/add | 물리 포트 분할, SF 생성 통합 |
| 펌웨어 | 벤더 전용 도구 | devlink dev flash | 표준화된 인터페이스 |
| 장애 관리 | dmesg 파싱 | devlink health show/diagnose | 구조화된 장애 정보 |
| 패킷 예외 | tcpdump 추측 | devlink trap show | HW 드롭 원인 코드 직접 확인 |
| 자원 관리 | 벤더별 sysfs | devlink resource show/set | 계층적 자원 트리 |
| 대역폭 | tc qdisc 개별 설정 | devlink rate set | VF/SF별 계층적 대역폭 보장 |
net/devlink/ 디렉터리에 있으며, 주요 파일은 dev.c, port.c, health.c, trap.c, resource.c, param.c, region.c, rate.c, linecard.c, sb.c입니다. 커널 6.x에서 단일 devlink.c가 여러 파일로 분리되었습니다.
인스턴스 생명주기
devlink 인스턴스는 네트워크 장치 드라이버의 probe() 함수에서 할당되어, remove()에서 해제됩니다. 커널 6.x 이후 devl_lock 기반 잠금이 도입되어 등록 순서와 동시성 제어가 정교해졌습니다.
/* devlink 인스턴스 생명주기 - 드라이버 probe/remove 패턴 */
static const struct devlink_ops my_dl_ops = {
.info_get = my_info_get,
.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
.reload_down = my_reload_down,
.reload_up = my_reload_up,
.port_type_set = my_port_type_set,
.flash_update = my_flash_update,
};
static int my_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct devlink *dl;
struct my_priv *priv;
int err;
/* 1단계: devlink 인스턴스 할당 */
dl = devlink_alloc(&my_dl_ops, sizeof(*priv), &pdev->dev);
if (!dl)
return -ENOMEM;
priv = devlink_priv(dl);
priv->pdev = pdev;
/* 2단계: 하위 객체 등록 (register 전) */
devl_lock(dl);
err = my_devlink_params_register(dl);
if (err)
goto err_unlock;
err = my_devlink_resources_register(dl);
if (err)
goto err_params;
err = my_devlink_ports_register(dl);
if (err)
goto err_resources;
err = my_health_reporters_create(dl);
if (err)
goto err_ports;
devl_unlock(dl);
/* 3단계: Netlink 노출 시작 */
devlink_register(dl);
pci_set_drvdata(pdev, dl);
return 0;
err_ports:
my_devlink_ports_unregister(dl);
err_resources:
my_devlink_resources_unregister(dl);
err_params:
my_devlink_params_unregister(dl);
err_unlock:
devl_unlock(dl);
devlink_free(dl);
return err;
}
static void my_remove(struct pci_dev *pdev)
{
struct devlink *dl = pci_get_drvdata(pdev);
/* 5단계: Netlink 노출 중단 */
devlink_unregister(dl);
/* 6단계: 하위 객체 해제 (역순) */
devl_lock(dl);
my_health_reporters_destroy(dl);
my_devlink_ports_unregister(dl);
my_devlink_resources_unregister(dl);
my_devlink_params_unregister(dl);
devl_unlock(dl);
/* 7단계: 메모리 해제 */
devlink_free(dl);
}
devlink_port_register() 같은 기존 API는 devl_port_register()로 대체되었습니다. 새로운 API는 호출자가 devl_lock을 잡은 상태에서 호출해야 합니다. 기존 API는 내부에서 lock을 획득하므로, 동일 경로에서 이중 잠금이 발생하지 않도록 주의해야 합니다.
| API | 용도 | 잠금 요구 | 커널 버전 |
|---|---|---|---|
devlink_alloc() | 인스턴스 할당 | 불필요 | 4.6+ |
devlink_register() | Netlink 노출 | 불필요 | 4.6+ |
devl_lock() / devl_unlock() | 인스턴스 잠금 | - | 6.1+ |
devl_port_register() | 포트 등록 (lock 아래) | devl_lock 필수 | 6.3+ |
devlink_unregister() | Netlink 노출 중단 | 불필요 | 4.6+ |
devlink_free() | 인스턴스 해제 | 불필요 | 4.6+ |
포트/리소스 모델
devlink 포트는 물리 포트뿐 아니라 PCI PF/VF, CPU 포트까지 표현합니다. 리소스 트리는 하드웨어 자원을 계층적으로 모델링해, 사전 점검 후 재분배를 지원합니다.
| 대상 | 예시 | 활용 | Netlink 속성 |
|---|---|---|---|
| port flavour | physical / pci_pf / pci_vf / cpu / pci_sf | 포트 정체성 구분 | DEVLINK_ATTR_PORT_FLAVOUR |
| port split | 4x25G / 2x50G / 1x100G | 물리 포트 대역폭 분할 | DEVLINK_ATTR_PORT_SPLIT_GROUP |
| resource | FDB, ACL, TCAM | 용량 할당, 검증 | DEVLINK_ATTR_RESOURCE_* |
| param | inline-mode, enable_roce | 드라이버별 런타임 제어 | DEVLINK_ATTR_PARAM_* |
포트 flavour 세부
포트 flavour는 devlink 포트의 종류를 구분합니다. 각 flavour는 다른 용도와 생명주기를 가집니다:
| Flavour | 설명 | 생성 방식 | netdev 연결 |
|---|---|---|---|
physical | 물리 네트워크 포트 | 하드웨어 고정 | enp3s0f0 등 |
cpu | CPU 포트 (제어면 연결) | 드라이버 자동 | 일반적으로 없음 |
dsa | DSA 스위치 포트 | DSA 프레임워크 | lan1, lan2 등 |
pci_pf | PCI Physical Function | PCI 열거 시 | 호스트 netdev |
pci_vf | PCI Virtual Function (SR-IOV) | sriov_numvfs 설정 | VF netdev / representor |
pci_sf | PCI Subfunction | devlink port add | SF netdev |
virtual | 가상 포트 | 드라이버 정의 | 드라이버별 상이 |
# 포트 전체 목록 및 flavour 확인
devlink port show
# 출력 예시:
# pci/0000:03:00.0/0: type eth netdev enp3s0f0np0 flavour physical port 0
# pci/0000:03:00.0/1: type eth netdev enp3s0f0np1 flavour physical port 1
# pci/0000:03:00.0/65536: type eth netdev enp3s0f0v0 flavour pci_vf controller 0 pfnum 0 vfnum 0
# JSON 형식으로 자동화에 적합한 출력
devlink -j port show | jq '.port | to_entries[] | {index: .key, flavour: .value.flavour, netdev: .value.netdev}'
# 리소스 트리 확인
devlink resource show pci/0000:03:00.0
# 출력 예시:
# pci/0000:03:00.0:
# name kvd size 245760 unit entry
# name linear size 98304 unit entry size_min 0 size_max 147456 size_gran 128
# name hash_double size 60416 unit entry size_min 0 size_max 147456 size_gran 128
# name hash_single size 87040 unit entry size_min 0 size_max 147456 size_gran 128
# 리소스 크기 변경 (reload 필요)
devlink resource set pci/0000:03:00.0 path kvd/linear size 131072
devlink dev reload pci/0000:03:00.0
devlink dev reload를 실행해야 적용됩니다. size_new 속성으로 미적용 변경 사항이 있는지 확인할 수 있습니다. size_min, size_max, size_gran을 확인하여 유효한 값만 설정하세요.
health reporter와 trap
장애 대응은 devlink 핵심 기능입니다. health reporter는 상태 점검 및 복구 훅을 제공하고, trap은 드롭 또는 예외 패킷을 원인 코드와 함께 관찰하게 해줍니다.
# 장치/포트 확인
devlink dev show
devlink port show
# health 상태 및 복구
devlink health show pci/0000:03:00.0
devlink health recover pci/0000:03:00.0 reporter fw
# trap 관찰
devlink trap show pci/0000:03:00.0
devlink trap group show pci/0000:03:00.0
eSwitch와 오프로드 통합
SR-IOV 환경에서는 devlink로 eSwitch 모드(legacy/switchdev)를 전환하고 representor 기반 오프로드 경로를 구성합니다. OVS/TC flower와 직접 연결되는 지점입니다.
# eSwitch 현재 모드 확인
devlink dev eswitch show pci/0000:03:00.0
# 출력: pci/0000:03:00.0: mode legacy
# switchdev 모드로 전환 (VF 해제 후 실행)
echo 0 > /sys/class/net/enp3s0f0/device/sriov_numvfs
devlink dev eswitch set pci/0000:03:00.0 mode switchdev
# VF 재생성 (representor 자동 생성)
echo 4 > /sys/class/net/enp3s0f0/device/sriov_numvfs
devlink port show
# eSwitch 인라인 모드 설정 (ConnectX-5 이하)
devlink dev eswitch set pci/0000:03:00.0 inline-mode transport
# encap 모드 설정 (VXLAN/Geneve 오프로드)
devlink dev eswitch set pci/0000:03:00.0 encap-mode basic
legacy에서 switchdev로 전환하면 기존 VF 설정이 초기화됩니다. 반드시 VF를 먼저 해제(sriov_numvfs=0)하고 전환해야 합니다. switchdev 모드에서는 VF의 MAC/VLAN 제어가 representor를 통해 이루어지므로, ip link set 기반 VF 설정이 동작하지 않을 수 있습니다.
드라이버 구현 패턴
devlink ops를 구현하는 드라이버는 구조체의 콜백을 채워야 합니다. 아래는 주요 콜백과 구현 요구사항을 정리한 것입니다.
| 콜백 | 용도 | 필수 여부 | 구현 복잡도 |
|---|---|---|---|
info_get | 드라이버/FW 버전 정보 | 권장 | 낮음 |
reload_down / reload_up | 장치 재초기화 | 선택 | 높음 |
flash_update | 펌웨어 갱신 | 선택 | 높음 |
port_type_set | 포트 타입 변경 | 선택 | 중간 |
port_split / port_unsplit | 포트 분할/복구 | 선택 | 중간 |
port_new / port_del | SF 포트 생성/삭제 | 선택 | 높음 |
port_fn_hw_addr_get/set | 포트 함수 MAC 설정 | 선택 | 낮음 |
port_fn_state_get/set | SF 상태 관리 | SF 지원 시 | 중간 |
trap_init / trap_fini | trap 초기화/정리 | trap 지원 시 | 중간 |
sb_pool_set | 공유 버퍼 풀 설정 | SB 지원 시 | 중간 |
/* info_get 콜백 구현 예 */
static int my_info_get(struct devlink *dl,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct my_priv *priv = devlink_priv(dl);
int err;
/* 드라이버 이름 */
err = devlink_info_driver_name_put(req, "my_driver");
if (err)
return err;
/* 시리얼 번호 (고유 식별자) */
err = devlink_info_serial_number_put(req, priv->serial);
if (err)
return err;
/* 펌웨어 버전 정보 (running vs stored) */
err = devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT,
priv->fw_version);
if (err)
return err;
err = devlink_info_version_stored_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT,
priv->fw_stored_version);
return err;
}
/* port 등록 예 */
static int my_devlink_ports_register(struct devlink *dl)
{
struct my_priv *priv = devlink_priv(dl);
struct devlink_port_attrs attrs = {};
int i, err;
for (i = 0; i < priv->num_ports; i++) {
memset(&attrs, 0, sizeof(attrs));
devlink_port_attrs_set(&priv->ports[i].dl_port, &attrs);
/* devl_lock이 이미 잡혀 있으므로 devl_ 접두사 사용 */
err = devl_port_register(dl, &priv->ports[i].dl_port, i);
if (err)
goto err_unregister;
}
return 0;
err_unregister:
for (i--; i >= 0; i--)
devl_port_unregister(&priv->ports[i].dl_port);
return err;
}
Generic Netlink ABI 심화
devlink는 단순 텍스트 CLI가 아니라 엄격한 Netlink ABI 위에서 동작합니다. 자동화 도구는 CLI 출력 문자열이 아니라 속성(attribute)을 기준으로 파싱해야 장기 호환성을 확보할 수 있습니다.
| 명령군 | 대표 명령 | 핵심 속성 | 운영 포인트 |
|---|---|---|---|
| device | DEVLINK_CMD_INFO_GET | driver_name, serial_number | 자산 식별 기준 |
| port | DEVLINK_CMD_PORT_GET | flavour, split_group, netdev_ifindex | 토폴로지 동기화 |
| resource | DEVLINK_CMD_RESOURCE_SET | name, size, size_new | reload 필요 여부 확인 |
| trap | DEVLINK_CMD_TRAP_GET | group, action, metadata | 드롭 원인 분류 |
| health | DEVLINK_CMD_HEALTH_REPORTER_GET | error, recover_count | 장애 자동 복구 지표 |
| reload/flash | DEVLINK_CMD_RELOAD, DEVLINK_CMD_FLASH_UPDATE | action, limit, component | 무중단 범위 제어 |
| param | DEVLINK_CMD_PARAM_GET/SET | name, value, cmode | 런타임/드라이버/영구 모드 |
| region | DEVLINK_CMD_REGION_GET | name, snapshot_id, address | 레지스터/메모리 덤프 |
| rate | DEVLINK_CMD_RATE_GET/SET | tx_share, tx_max, parent | 계층적 대역폭 제어 |
| linecard | DEVLINK_CMD_LINECARD_GET | state, type, index | 모듈형 라인카드 관리 |
# JSON 출력으로 파싱 안정성 확보
devlink -j dev show | jq .
devlink -j port show pci/0000:03:00.0 | jq .
devlink -j resource show pci/0000:03:00.0 | jq .
devlink -j trap show pci/0000:03:00.0 | jq .
# 머신파서가 문자열 대신 키를 사용하도록 고정
devlink -j health show pci/0000:03:00.0 | jq '.[][] | {name,error,recover_count}'
# 장치 정보 조회 (드라이버/FW 버전)
devlink dev info pci/0000:03:00.0
# 출력 예시:
# pci/0000:03:00.0:
# driver mlx5_core
# serial_number MT2116X09299
# versions:
# fixed:
# fw.psid MT_0000000228
# running:
# fw.mgmt 22.36.1010
# stored:
# fw.mgmt 22.36.1010
# Netlink 모니터링 (이벤트 실시간 수신)
devlink monitor all
devlink monitor all 명령은 포트 추가/삭제, trap 변경, health 이벤트 등을 실시간으로 수신합니다. 자동화 시스템에서 이벤트 기반 반응을 구현할 때 필수적인 도구입니다. devlink monitor trap처럼 특정 이벤트만 필터링할 수도 있습니다.
포트 토폴로지와 split/subfunction
실무에서 가장 많이 실수하는 지점은 포트 정체성을 netdev 이름으로만 추적하는 것입니다. devlink 포트 인덱스와 flavour를 기준으로 추적해야 split, VF 생성, SF(Subfunction) 생성 이후에도 일관성이 유지됩니다.
# 포트 분할/복구 (드라이버 지원 시)
devlink port show pci/0000:03:00.0
devlink port split pci/0000:03:00.0/1 count 4
devlink dev reload pci/0000:03:00.0 action driver_reinit
devlink port show pci/0000:03:00.0
devlink port unsplit pci/0000:03:00.0/1
# SF 예시(드라이버 지원 시)
devlink port add pci/0000:03:00.0 flavour pcisf pfnum 0 sfnum 11
devlink port function set pci/0000:03:00.0/32768 hw_addr 00:11:22:33:44:55 state active
subfunction (SF) 심화
Subfunction은 PCI VF의 한계를 넘어 더 가볍고 유연한 하드웨어 격리를 제공하는 메커니즘입니다. VF가 PCI 레벨 함수인 반면, SF는 같은 PF 안에서 소프트웨어적으로 격리된 함수입니다. mlx5 드라이버에서 처음 구현되었으며, 컨테이너/마이크로서비스 환경에서 네트워크 리소스 격리에 활용됩니다.
# SF 생성 전체 절차
# 1. SF 포트 생성
devlink port add pci/0000:03:00.0 flavour pcisf pfnum 0 sfnum 88
# 출력: pci/0000:03:00.0/32768: type eth netdev ... flavour pcisf ...
# 2. MAC 주소 설정 (active 전에)
devlink port function set pci/0000:03:00.0/32768 \
hw_addr 00:00:00:00:88:88
# 3. SF 활성화 (auxiliary bus probe 발생)
devlink port function set pci/0000:03:00.0/32768 state active
# 4. 확인
devlink port show pci/0000:03:00.0/32768
devlink port function show pci/0000:03:00.0/32768
# 5. SF에 대한 devlink 인스턴스 확인
devlink dev show
# auxiliary/mlx5_core.sf.2 등으로 나타남
# 6. SF 비활성화 및 삭제
devlink port function set pci/0000:03:00.0/32768 state inactive
devlink port del pci/0000:03:00.0/32768
active 상태로 전환되면 auxiliary bus에 디바이스가 등록됩니다. mlx5의 경우 mlx5_core.sf.N이라는 이름으로 등록되며, 이 디바이스에 대한 별도의 devlink 인스턴스가 생성됩니다. 따라서 SF는 자체적인 health reporter, trap, param을 가질 수 있습니다.
devlink-param 심화
devlink-param은 드라이버별 설정 파라미터를 표준화된 인터페이스로 노출합니다. 각 파라미터는 세 가지 설정 모드(cmode)를 가질 수 있으며, 이를 통해 런타임 설정과 영구 설정을 분리합니다.
| 설정 모드 (cmode) | 설명 | 적용 시점 | 재시작 후 |
|---|---|---|---|
runtime | 즉시 적용 | 명령 실행 즉시 | 초기화 |
driverinit | 드라이버 재초기화 시 적용 | reload 후 | 초기화 |
permanent | NVM에 저장 | 재부팅 후 | 유지 |
# 전체 파라미터 조회
devlink dev param show pci/0000:03:00.0
# 출력 예시:
# pci/0000:03:00.0:
# name enable_roce type generic
# values:
# cmode driverinit value true
# name internal_err_reset type generic
# values:
# cmode runtime value true
# 특정 파라미터 조회
devlink dev param show pci/0000:03:00.0 name enable_roce
# 파라미터 설정 (runtime)
devlink dev param set pci/0000:03:00.0 \
name internal_err_reset value false cmode runtime
# 파라미터 설정 (driverinit - reload 필요)
devlink dev param set pci/0000:03:00.0 \
name enable_roce value false cmode driverinit
devlink dev reload pci/0000:03:00.0
주요 Generic 파라미터
| 파라미터 | 타입 | 지원 cmode | 설명 |
|---|---|---|---|
enable_roce | bool | driverinit | RoCE(RDMA over Converged Ethernet) 활성화 |
enable_eth | bool | driverinit | 이더넷 기능 활성화 |
enable_iwarp | bool | driverinit | iWARP 활성화 |
internal_err_reset | bool | runtime | 내부 오류 시 자동 리셋 |
max_macs | u32 | driverinit | 최대 MAC 주소 수 |
region_snapshot_enable | bool | runtime | region 스냅샷 자동 생성 |
enable_remote_dev_reset | bool | runtime | 원격 장치 리셋 허용 |
enable_vnet | bool | driverinit | VirtIO-net 에뮬레이션 활성화 |
/* 드라이버에서 param 등록하기 */
static const struct devlink_param my_params[] = {
DEVLINK_PARAM_GENERIC(ENABLE_ROCE,
BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
NULL, NULL, NULL),
DEVLINK_PARAM_DRIVER(100, "my_custom_param",
DEVLINK_PARAM_TYPE_U32,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
my_param_get, my_param_set, NULL),
};
/* probe 시 등록 */
err = devl_params_register(dl, my_params, ARRAY_SIZE(my_params));
/* driverinit 초기값 설정 */
union devlink_param_value val;
val.vbool = true;
devl_param_driverinit_value_set(dl,
DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE, val);
devlink-region 심화
devlink-region은 NIC의 내부 메모리, 레지스터 공간, 또는 펌웨어 구성을 사용자 공간에서 읽을 수 있도록 하는 인터페이스입니다. 주로 디버깅과 장애 분석에 사용되며, 스냅샷을 통해 특정 시점의 상태를 보존할 수 있습니다.
# region 목록 확인
devlink region show
# 출력 예시:
# pci/0000:03:00.0/cr-space: size 1048576 snapshot [0 1]
# pci/0000:03:00.0/fw-health: size 64 snapshot [0]
# 스냅샷 생성
devlink region snapshot new pci/0000:03:00.0/cr-space
# 스냅샷 데이터 읽기 (주소 범위 지정)
devlink region dump pci/0000:03:00.0/cr-space snapshot 0
devlink region read pci/0000:03:00.0/cr-space snapshot 0 \
address 0x1000 length 256
# 스냅샷 삭제
devlink region del pci/0000:03:00.0/cr-space snapshot 0
# 직접 읽기 (스냅샷 없이, 지원 시)
devlink region read pci/0000:03:00.0/cr-space address 0 length 64
cr-space(Configuration Register 공간), fw-health(펌웨어 상태), icm-event(ICM 이벤트 로그) 등의 region을 제공합니다. 장애 발생 시 cr-space 스냅샷을 벤더에 전달하면 하드웨어 레벨 디버깅이 가능합니다.
/* 드라이버에서 region 등록 */
static const struct devlink_region_ops my_cr_region_ops = {
.name = "cr-space",
.snapshot = my_cr_snapshot,
.destructor = my_cr_snapshot_destroy,
.read = my_cr_direct_read, /* 직접 읽기 지원 시 */
};
/* probe 시 등록 */
priv->cr_region = devl_region_create(dl, &my_cr_region_ops,
1, /* max_snapshots */
cr_space_size);
/* health reporter에서 자동 스냅샷 생성 */
static int my_fw_reporter_dump(struct devlink_health_reporter *reporter,
struct devlink_fmsg *fmsg,
void *priv_ctx,
struct netlink_ext_ack *extack)
{
struct my_priv *priv = reporter->priv;
/* region 스냅샷을 자동으로 생성하여 장애 상태 보존 */
devlink_region_snapshot_id_get(priv->devlink, &snapshot_id);
devlink_region_snapshot_create(priv->cr_region, data, snapshot_id);
return 0;
}
trap 파이프라인과 원인 분류
trap은 단순 카운터가 아니라 제어면으로 전달되는 예외 이벤트 파이프라인입니다. 드롭 원인별 우선순위를 나눠야 경보 폭주를 막고 실제 장애 신호를 살릴 수 있습니다.
주요 trap 그룹과 trap
| 그룹 | 대표 trap | 기본 action | 설명 |
|---|---|---|---|
l2_drops | source_mac_is_multicast | drop | 출발지 MAC이 멀티캐스트인 프레임 |
l2_drops | vlan_tag_mismatch | drop | VLAN 태그 불일치 |
l2_drops | ingress_vlan_filter | drop | 입력 VLAN 필터 위반 |
l3_drops | blackhole_route | drop | 블랙홀 경로로의 패킷 |
l3_drops | ttl_value_is_too_small | trap | TTL 소진 패킷 (ICMP 응답 필요) |
l3_drops | non_routable | drop | 라우팅 불가 패킷 |
l3_exceptions | mtu_value_is_too_small | trap | MTU 초과 (ICMP 필요) |
buffer_drops | tail_drop | drop | 버퍼 오버플로우 |
acl_drops | ingress_flow_action_drop | drop | ACL 규칙에 의한 드롭 |
stp | stp | trap | STP BPDU 패킷 |
ospf | ospf | trap | OSPF 프로토콜 패킷 |
bgp | bgp | trap | BGP 프로토콜 패킷 |
# trap 그룹/개별 정책 확인
devlink trap group show pci/0000:03:00.0
devlink trap show pci/0000:03:00.0
# 특정 trap 통계 확인
devlink -s trap show pci/0000:03:00.0 trap source_mac_is_multicast
# 출력 예시:
# pci/0000:03:00.0:
# name source_mac_is_multicast type drop
# stats:
# rx:
# bytes 0 packets 0
# drops:
# bytes 1234 packets 10
# trap action 변경 (droppoly -> trap로 전환하여 패킷 수집)
devlink trap set pci/0000:03:00.0 trap ttl_value_is_too_small action trap
devlink trap set pci/0000:03:00.0 trap blackhole_route action drop
# trap policer 설정 (제어면 보호)
devlink trap policer set pci/0000:03:00.0 policer 1 rate 1000 burst 128
# trap group에 policer 연결
devlink trap group set pci/0000:03:00.0 group l3_drops policer 1
trap으로 설정하면 해당 패킷이 CPU로 전달됩니다. DDoS 공격 등으로 대량의 예외 패킷이 발생할 경우 CPU 과부하를 초래할 수 있으므로, 반드시 policer를 설정하여 rate/burst를 제한해야 합니다.
health reporter 실전 설계
health reporter는 "문제 발견"보다 "안전한 복구 경계"를 어디에 두는지가 핵심입니다. recover 훅은 강력하지만 데이터면 영향이 크므로 단계별 복구 전략이 필요합니다.
| 리포터 | 관측 대상 | 일반 복구 전략 | 위험도 | 지원 드라이버 |
|---|---|---|---|---|
| fw | 펌웨어 assert, command timeout | fw reset -> driver reinit | 중간~높음 | mlx5, ice |
| fw_fatal | FW 크래시, 복구 불가 오류 | 전체 리셋 | 높음 | mlx5 |
| tx | 큐 정지, completion stall | queue reset | 중간 | mlx5 |
| rx | RX 큐 정지, 타임아웃 | queue reset | 중간 | mlx5 |
| pci | AER, 링크 다운 | function reset | 높음 | ice |
| internal | 드라이버 자체 상태 이상 | soft recover | 낮음~중간 | 다수 |
| vnic | VF/SF의 가상 NIC 상태 | 진단 전용 | 낮음 | mlx5 |
# reporter 상태 확인
devlink health show pci/0000:03:00.0
# 출력 예시:
# pci/0000:03:00.0:
# reporter fw
# state healthy error 0 recover 0 grace_period 60000 auto_recover true auto_dump true
# reporter fw_fatal
# state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
# reporter tx
# state healthy error 2 recover 2
# dump 수집 (장애 직후 보존)
devlink health dump show pci/0000:03:00.0 reporter fw
# 진단 정보 확인
devlink health diagnose pci/0000:03:00.0 reporter fw
# 수동 복구
devlink health recover pci/0000:03:00.0 reporter fw
# 자동 복구 비활성화 (디버깅 시)
devlink health set pci/0000:03:00.0 reporter fw auto_recover false
# grace period 설정 (밀리초)
devlink health set pci/0000:03:00.0 reporter fw grace_period 120000
/* health reporter 구현 예 */
static int my_fw_reporter_recover(
struct devlink_health_reporter *reporter,
void *priv_ctx,
struct netlink_ext_ack *extack)
{
struct my_priv *priv = devlink_health_reporter_priv(reporter);
/* 펌웨어 리셋 실행 */
my_fw_reset(priv);
/* 드라이버 재초기화 */
my_reinit(priv);
return 0;
}
static int my_fw_reporter_dump(
struct devlink_health_reporter *reporter,
struct devlink_fmsg *fmsg,
void *priv_ctx,
struct netlink_ext_ack *extack)
{
struct my_priv *priv = devlink_health_reporter_priv(reporter);
/* 구조화된 덤프 정보 작성 */
devlink_fmsg_obj_nest_start(fmsg);
devlink_fmsg_put_name(fmsg, "fw_version");
devlink_fmsg_put_value(fmsg, priv->fw_ver);
devlink_fmsg_put_name(fmsg, "error_code");
devlink_fmsg_u32_put(fmsg, priv->last_error);
devlink_fmsg_obj_nest_end(fmsg);
return 0;
}
static const struct devlink_health_reporter_ops my_fw_reporter_ops = {
.name = "fw",
.recover = my_fw_reporter_recover,
.dump = my_fw_reporter_dump,
};
/* probe 시 reporter 생성 */
priv->fw_reporter = devl_health_reporter_create(dl,
&my_fw_reporter_ops,
60000, /* grace_period_ms */
priv);
/* 오류 발생 시 보고 */
devlink_health_report(priv->fw_reporter, "FW assert detected", &ctx);
reload/flash 절차 심화
reload와 flash는 가장 위험한 유지보수 동작입니다. 반드시 "지원 action/limit 조회 -> 사전 스냅샷 -> 단계별 실행 -> 사후 검증" 순서를 자동화해야 합니다.
# 지원 reload 액션/limit 확인
devlink dev info pci/0000:03:00.0
# reload 지원 여부는 드라이버 capabilities에 의존
# 드라이버 재초기화 기반 reload
devlink dev reload pci/0000:03:00.0 action driver_reinit
# FW activate (flash 후 재부팅 없이 적용)
devlink dev reload pci/0000:03:00.0 action fw_activate
# no_reset limit 적용 (데이터면 중단 최소화)
devlink dev reload pci/0000:03:00.0 action fw_activate limit no_reset
# flash (드라이버 지원 component 사용)
devlink dev flash pci/0000:03:00.0 file /var/tmp/fw.bin component fw.mgmt
# flash 진행률 모니터링 (별도 터미널)
devlink monitor
/* reload 콜백 구현 */
static int my_reload_down(struct devlink *dl,
bool netns_change,
enum devlink_reload_action action,
enum devlink_reload_limit limit,
struct netlink_ext_ack *extack)
{
struct my_priv *priv = devlink_priv(dl);
switch (action) {
case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:
/* 데이터면 정지 */
my_stop_data_path(priv);
/* 하드웨어 자원 해제 */
my_teardown_hw(priv);
break;
case DEVLINK_RELOAD_ACTION_FW_ACTIVATE:
if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET)
return my_fw_activate_no_reset(priv);
return my_fw_activate(priv);
default:
return -EOPNOTSUPP;
}
return 0;
}
static int my_reload_up(struct devlink *dl,
enum devlink_reload_action action,
enum devlink_reload_limit limit,
u32 *actions_performed,
struct netlink_ext_ack *extack)
{
struct my_priv *priv = devlink_priv(dl);
/* driverinit param 적용 */
my_apply_driverinit_params(priv);
/* 하드웨어 재초기화 */
my_setup_hw(priv);
/* 데이터면 재시작 */
my_start_data_path(priv);
*actions_performed = BIT(action);
return 0;
}
action driver_reinit은 드라이버를 완전히 재초기화합니다. 이 과정에서 in-flight 패킷이 손실될 수 있습니다. 프로덕션 환경에서는 반드시 트래픽을 다른 경로로 우회(drain)시킨 후 실행하세요. limit no_reset을 지원하는 드라이버는 데이터면 중단을 최소화할 수 있습니다.
rate / shared-buffer / queue 연계
대규모 멀티테넌트 환경에서는 링크 대역폭만 제어하면 부족합니다. devlink rate와 shared buffer를 함께 사용해 혼잡 도메인을 분리해야 안정적입니다.
| 기능 | 목표 | 대표 지표 | 문제 징후 |
|---|---|---|---|
| rate node | VF/SF별 최소/최대 대역폭 | tx_bw, tx_share | 특정 tenant 독점 |
| shared buffer | 우발 burst 흡수 | pool occupancy | tail drop 급증 |
| queue depth | 큐 지연 관리 | cq overrun, timeout | latency 급등 |
# rate node 생성 및 설정
# 1. 중간 노드 생성 (tenant 그룹)
devlink port function rate add pci/0000:03:00.0/tenant-a
# 2. 중간 노드에 대역폭 설정
devlink port function rate set pci/0000:03:00.0/tenant-a \
tx_share 40gbit tx_max 60gbit
# 3. VF/SF를 중간 노드에 연결
devlink port function rate set pci/0000:03:00.0/65536 \
tx_share 5gbit tx_max 10gbit parent tenant-a
# 4. rate tree 확인
devlink port function rate show pci/0000:03:00.0
# shared buffer 관리
devlink sb show pci/0000:03:00.0
devlink sb pool show pci/0000:03:00.0 sb 0
# sb pool 크기 조정
devlink sb pool set pci/0000:03:00.0 sb 0 pool 0 size 8388608 \
thtype static
# sb occupancy 실시간 확인
devlink sb occupancy show pci/0000:03:00.0
devlink sb occupancy snapshot pci/0000:03:00.0
# tc-port 바인딩
devlink sb tc bind set pci/0000:03:00.0/1 sb 0 tc 0 type ingress \
th 6 pool 0
devlink-linecard 심화
devlink-linecard는 모듈형 네트워크 장비(섀시 스위치)의 라인카드를 관리하는 인터페이스입니다. 라인카드의 삽입/제거, 타입 설정, 프로비저닝 상태를 제어합니다. Mellanox SN3700 등의 모듈형 스위치에서 사용됩니다.
# 라인카드 상태 확인
devlink lc show pci/0000:03:00.0
# 출력 예시:
# pci/0000:03:00.0:
# lc 1 state unprovisioned supported_types: 16x100G 32x50G 4x400G
# 라인카드 타입 프로비저닝
devlink lc set pci/0000:03:00.0 lc 1 type 16x100G
# 프로비저닝 해제
devlink lc set pci/0000:03:00.0 lc 1 notype
드라이버별 구현 비교 (mlx5 / ice / bnxt)
devlink의 실질적인 기능 범위는 드라이버마다 크게 다릅니다. 운영 자동화 스크립트를 작성할 때는 대상 드라이버의 지원 범위를 반드시 확인해야 합니다.
| 기능 | mlx5 (ConnectX-6+) | ice (E810) | bnxt (BCM57500+) | nfp (Agilio) |
|---|---|---|---|---|
| info_get | O (상세) | O (상세) | O | O |
| port split | O | O | X | O |
| subfunction (SF) | O | O (제한적) | X | X |
| eSwitch mode | O | O | X | O |
| param | O (다수) | O | O | O |
| resource | X | X | X | O (상세) |
| health reporter | O (fw/tx/rx/vnic) | O (fw/pci) | O (fw) | O |
| trap | O (mlxsw) | O | X | O |
| region | O (cr-space) | O (nvm/caps) | X | X |
| rate | O | X | X | X |
| linecard | O (mlxsw) | X | X | X |
| flash update | O | O | O | O |
| reload | O (reinit+fw_activate) | O (reinit) | O (reinit) | X |
mlx5 드라이버 devlink 구현 상세
# mlx5 전체 devlink 정보
devlink dev info pci/0000:03:00.0
# 출력 예시:
# pci/0000:03:00.0:
# driver mlx5_core
# serial_number MT2116X09299
# versions:
# fixed:
# fw.psid MT_0000000228
# board.id-revid LNV0000000032
# running:
# fw.mgmt 22.36.1010
# fw.undi 14.29.15
# fw.bundle_id 22360_1010
# stored:
# fw.mgmt 22.36.1010
# fw.undi 14.29.15
# mlx5 health reporter 목록
devlink health show pci/0000:03:00.0
# fw, fw_fatal, vnic, tx, rx 리포터 제공
# mlx5 VNIC 진단 (VF/SF별 상태 진단)
devlink health diagnose pci/0000:03:00.0 reporter vnic
# mlx5 region (cr-space) 스냅샷
devlink region show pci/0000:03:00.0
devlink region snapshot new pci/0000:03:00.0/cr-space
# mlx5 SF 생성 예
devlink port add pci/0000:03:00.0 flavour pcisf pfnum 0 sfnum 4
devlink port function set pci/0000:03:00.0/32768 \
hw_addr 02:00:00:00:00:04 state active
# mlx5 rate 설정
devlink port function rate add pci/0000:03:00.0/group-1
devlink port function rate set pci/0000:03:00.0/group-1 \
tx_share 10gbit tx_max 25gbit
devlink port function rate set pci/0000:03:00.0/32768 \
tx_max 5gbit parent group-1
ice 드라이버 devlink 구현 상세
# ice 장치 정보
devlink dev info pci/0000:31:00.0
# 출력 예시:
# pci/0000:31:00.0:
# driver ice
# serial_number 00-01-02-03-04-05
# versions:
# fixed:
# board.id K94210-000
# running:
# fw.mgmt 3.2.5
# fw.undi 2.1.0
# fw.netlist 3.2.5-1
# ice 포트 분할
devlink port split pci/0000:31:00.0/0 count 4
# ice reload (driverinit param 적용)
devlink dev param set pci/0000:31:00.0 \
name enable_roce value false cmode driverinit
devlink dev reload pci/0000:31:00.0
# ice flash update
devlink dev flash pci/0000:31:00.0 file /tmp/ice_fw.bin
# ice eSwitch 모드
devlink dev eswitch set pci/0000:31:00.0 mode switchdev
# ice region (NVM/Capabilities)
devlink region show pci/0000:31:00.0
devlink region dump pci/0000:31:00.0/nvm-flash snapshot 0
drivers/net/ethernet/mellanox/mlx5/core/devlink.c, ice는 drivers/net/ethernet/intel/ice/ice_devlink.c, bnxt는 drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c에 있습니다. 각 드라이버의 devlink 구현을 참고하면 자체 드라이버에 기능을 추가할 때 도움이 됩니다.
운영 자동화 패턴
devlink는 사람이 직접 조작하는 횟수를 줄이고, 상태 수집과 검증을 자동화할 때 효과가 큽니다. 특히 장치 수가 많아질수록 "명령 실행"보다 "사전/사후 검증 규칙"이 중요합니다.
#!/usr/bin/env bash
# devlink 종합 자동화 스크립트 (사전/사후 검증 포함)
set -euo pipefail
DEV="${1:-pci/0000:03:00.0}"
ACTION="${2:-snapshot}" # snapshot | reload | flash
FW_FILE="${3:-}"
TS="$(date +%Y%m%d-%H%M%S)"
OUT="/var/tmp/devlink-${TS}"
mkdir -p "$OUT"
# 함수: JSON 스냅샷 수집
snapshot() {
local suffix="${1:-before}"
devlink -j dev show "$DEV" > "$OUT/dev.${suffix}.json"
devlink -j dev info "$DEV" > "$OUT/info.${suffix}.json"
devlink -j port show "$DEV" > "$OUT/port.${suffix}.json"
devlink -j health show "$DEV" > "$OUT/health.${suffix}.json"
devlink -j trap show "$DEV" > "$OUT/trap.${suffix}.json" 2>/dev/null || true
devlink -j resource show "$DEV" > "$OUT/resource.${suffix}.json" 2>/dev/null || true
echo "[INFO] snapshot saved: $OUT/*.${suffix}.json"
}
# 함수: 사전 검증
pre_validate() {
local health_errors
health_errors=$(jq '[.[][] | .error // 0] | add' "$OUT/health.before.json")
if [ "$health_errors" -gt 0 ]; then
echo "[WARN] 기존 health 오류 ${health_errors}건 존재"
fi
echo "[INFO] FW version: $(jq -r '.info[][].versions.running."fw.mgmt" // "N/A"' "$OUT/info.before.json")"
echo "[INFO] 포트 수: $(jq '.port | length' "$OUT/port.before.json")"
}
# 함수: 사후 검증
post_validate() {
snapshot "after"
# 포트 수 변동 확인
local before_ports after_ports
before_ports=$(jq '.port | length' "$OUT/port.before.json")
after_ports=$(jq '.port | length' "$OUT/port.after.json")
if [ "$before_ports" != "$after_ports" ]; then
echo "[ALERT] 포트 수 변동: ${before_ports} -> ${after_ports}"
fi
# health error 증가 확인
local before_err after_err
before_err=$(jq '[.[][] | .error // 0] | add' "$OUT/health.before.json")
after_err=$(jq '[.[][] | .error // 0] | add' "$OUT/health.after.json")
if [ "$after_err" -gt "$before_err" ]; then
echo "[ALERT] health 오류 증가: ${before_err} -> ${after_err}"
fi
echo "[INFO] 사후 검증 완료"
}
# 메인 로직
echo "[INFO] devlink automation: DEV=$DEV ACTION=$ACTION"
snapshot "before"
pre_validate
case "$ACTION" in
snapshot)
echo "[INFO] 스냅샷만 수집합니다."
;;
reload)
echo "[INFO] reload 실행..."
devlink dev reload "$DEV" action driver_reinit
post_validate
;;
flash)
if [ -z "$FW_FILE" ]; then
echo "[ERROR] flash 파일 경로를 지정하세요."
exit 1
fi
echo "[INFO] flash 실행: $FW_FILE"
devlink dev flash "$DEV" file "$FW_FILE"
post_validate
;;
*)
echo "[ERROR] 알 수 없는 ACTION: $ACTION"
exit 1
;;
esac
echo "[INFO] 결과: $OUT"
pyroute2를 이용한 Python 자동화
# pyroute2를 이용한 devlink 자동화 예제
import json
import subprocess
def devlink_json(cmd):
"""devlink 명령을 JSON으로 실행하여 파싱된 결과 반환"""
result = subprocess.run(
["devlink", "-j"] + cmd.split(),
capture_output=True, text=True, check=True
)
return json.loads(result.stdout)
def get_health_status(dev):
"""health reporter 상태를 구조화하여 반환"""
data = devlink_json(f"health show {dev}")
reporters = {}
for key, items in data.get("health", {}).items():
for item in items:
reporters[item["name"]] = {
"state": item.get("state", "unknown"),
"error": item.get("error", 0),
"recover": item.get("recover", 0),
}
return reporters
def get_port_topology(dev):
"""포트 토폴로지를 flavour별로 분류하여 반환"""
data = devlink_json(f"port show {dev}")
topology = {}
for port_id, info in data.get("port", {}).items():
flavour = info.get("flavour", "unknown")
if flavour not in topology:
topology[flavour] = []
topology[flavour].append({
"id": port_id,
"netdev": info.get("netdev"),
"type": info.get("type"),
})
return topology
# 사용 예
dev = "pci/0000:03:00.0"
health = get_health_status(dev)
for name, status in health.items():
if status["error"] > 0:
print(f"[ALERT] reporter {name}: {status['error']} errors")
ports = get_port_topology(dev)
print(f"포트 요약: {', '.join(f'{k}: {len(v)}개' for k, v in ports.items())}")
운영 체크리스트
프로덕션 환경에서 devlink를 활용할 때 반드시 확인해야 하는 항목들을 정리합니다.
일상 모니터링
- health reporter 상태:
devlink health show로 모든 reporter의error/recover_count를 주기적으로 확인합니다. - trap 변화율: trap 이벤트는 단순 카운트가 아니라 발생률 변화를 기준으로 경보를 설정합니다. 절대값보다 delta/s가 유용합니다.
- 포트 상태:
devlink port show의 포트 수와 flavour 분포가 예상과 일치하는지 확인합니다. - sb occupancy: shared buffer 점유율이 임계치(80%)를 넘으면 경보를 발생시킵니다.
유지보수 전 확인
- 펌웨어 업그레이드 전 resource snapshot를 확보해 롤백 기준을 남깁니다.
- reload 가능 범위를 문서화하고, 데이터면 중단 영향(패킷 손실 창)을 측정합니다.
- eSwitch 모드 전환은 OVS/TC 규칙 동기화 절차와 함께 자동화합니다.
- devlink dev info로 현재 running/stored FW 버전을 확인합니다.
- 대상 장치의 health error_count가 0인지 확인합니다.
장애 대응 절차
- 1단계:
devlink health diagnose로 해당 reporter의 상태 정보를 수집합니다. - 2단계:
devlink health dump show로 상세 덤프를 확인합니다. - 3단계: region 스냅샷을 생성하여 하드웨어 레벨 상태를 보존합니다.
- 4단계: 자동 복구가 비활성화되어 있다면
devlink health recover로 수동 복구를 시도합니다. - 5단계: 복구 실패 시
devlink dev reload를 검토합니다.
devlink -j 출력을 주기적으로 수집하는 exporter를 작성하는 것이 효과적입니다. health error_count, trap counter, sb occupancy를 시계열 메트릭으로 수집하면 장애 징후를 사전에 탐지할 수 있습니다.
네트워크 네임스페이스와 devlink
커널 5.x 이후 devlink는 네트워크 네임스페이스 이동을 지원합니다. 컨테이너 환경에서 SF나 VF를 특정 네임스페이스에 격리하여 운영할 수 있습니다.
# devlink 인스턴스를 특정 네임스페이스로 이동
devlink dev reload pci/0000:03:00.0 netns my_container_ns
# 특정 네임스페이스에서 devlink 명령 실행
ip netns exec my_container_ns devlink dev show
ip netns exec my_container_ns devlink port show
# SF를 네임스페이스로 이동하여 격리
# 1. SF 생성 후 활성화
devlink port add pci/0000:03:00.0 flavour pcisf pfnum 0 sfnum 10
devlink port function set pci/0000:03:00.0/32768 \
hw_addr 02:00:00:00:00:10 state active
# 2. SF의 auxiliary devlink를 네임스페이스로 이동
devlink dev reload auxiliary/mlx5_core.sf.0 netns tenant_ns
# 3. 네임스페이스 내에서 SF 독립 운영
ip netns exec tenant_ns devlink dev show
ip netns exec tenant_ns devlink health show auxiliary/mlx5_core.sf.0
커널 설정
| 옵션 | 설명 | 권장 | 비고 |
|---|---|---|---|
CONFIG_NET_DEVLINK | devlink 코어 | y | 대부분 자동 선택됨 |
CONFIG_NET_SWITCHDEV | switchdev 오프로드 | y | eSwitch 사용 시 필수 |
CONFIG_MLX5_CORE | Mellanox ConnectX 시리즈 | m | 가장 풍부한 devlink 지원 |
CONFIG_MLX5_ESWITCH | mlx5 eSwitch 지원 | y | switchdev 모드 필수 |
CONFIG_MLX5_SF | mlx5 Subfunction 지원 | y | SF 사용 시 필수 |
CONFIG_ICE | Intel E810 계열 | m | ice devlink 지원 |
CONFIG_BNXT | Broadcom NetXtreme | m | bnxt devlink 지원 |
CONFIG_NFP | Netronome Agilio | m | resource 모델 참조 구현 |
CONFIG_MLXSW_CORE | Mellanox Spectrum 스위치 | m | trap/linecard 참조 구현 |
CONFIG_AUXILIARY_BUS | auxiliary bus | y | SF 사용 시 필수 |
# devlink 관련 커널 설정 확인
grep -E 'DEVLINK|SWITCHDEV|MLX5|ICE|BNXT|NFP|MLXSW|AUXILIARY' /boot/config-$(uname -r)
# iproute2 패키지 확인 (devlink CLI 포함)
devlink --version
# devlink utility, iproute2-6.x.0
# 필요한 커널 모듈 로드
modprobe devlink
modprobe mlx5_core
modprobe ice
문제 해결과 디버깅
devlink 관련 문제를 진단할 때 체계적인 접근이 필요합니다. 아래는 자주 발생하는 문제와 해결 방법을 정리합니다.
| 증상 | 원인 | 해결 방법 |
|---|---|---|
devlink dev show 빈 출력 | devlink 지원 드라이버 미로드 | modprobe mlx5_core 등 드라이버 로드 |
| eSwitch 전환 실패 | VF가 아직 활성 상태 | sriov_numvfs=0 후 전환 |
| SF 생성 실패 | CONFIG_MLX5_SF=n | 커널 설정 확인 및 재빌드 |
| reload 실패 | 지원하지 않는 action/limit | devlink dev info로 지원 범위 확인 |
| flash 진행 중단 | FW 파일 손상/호환성 | 벤더 제공 FW 파일 확인, MD5 검증 |
| trap 카운터 안 올라감 | 드라이버 trap 미지원 | 드라이버 소스에서 trap 등록 확인 |
| health dump 비어있음 | reporter의 dump 콜백 미구현 | diagnose 사용 또는 드라이버 확인 |
| rate 설정 실패 | 드라이버 미지원 | mlx5만 rate 지원 (현재) |
| port show에 netdev 없음 | 네트워크 드라이버 미바인딩 | auxiliary bus 드라이버 확인 |
# 디버깅 도구
# 1. Netlink 메시지 추적
modprobe netlink_diag
ss -f netlink -a
# 2. devlink 이벤트 모니터링
devlink monitor all &
# 별도 터미널에서 작업 수행
# 3. dmesg에서 devlink/드라이버 관련 로그
dmesg | grep -iE 'devlink|mlx5|ice|bnxt'
# 4. Netlink 디버그 (커널 디버그 활성화 시)
echo 1 > /sys/kernel/debug/tracing/events/devlink/enable
cat /sys/kernel/debug/tracing/trace_pipe
# 5. health dump를 파일로 저장
devlink health dump show pci/0000:03:00.0 reporter fw > /tmp/fw_dump.json
# 6. 모든 devlink 정보 종합 수집
for cmd in "dev show" "dev info" "port show" "health show" \
"trap show" "resource show" "param show"; do
echo "=== devlink $cmd ==="
devlink -j $cmd 2>/dev/null || echo "(미지원)"
done
echo devlink_* > /sys/kernel/debug/tracing/set_ftrace_filter로 devlink 관련 함수만 필터링하면, 특정 Netlink 명령이 어떤 ops 콜백을 호출하는지 정확히 추적할 수 있습니다. 자세한 내용은 ftrace 문서를 참고하세요.
devlink tracepoint 활용
커널은 devlink 관련 tracepoint를 제공합니다. 이를 활용하면 Netlink 명령 수준이 아닌 커널 내부 동작 수준에서 devlink의 동작을 추적할 수 있습니다.
# devlink tracepoint 목록 확인
ls /sys/kernel/debug/tracing/events/devlink/
# devlink_hwmsg, devlink_hwerr, devlink_health_report,
# devlink_health_recover_aborted, devlink_trap_report 등
# health report tracepoint 활성화
echo 1 > /sys/kernel/debug/tracing/events/devlink/devlink_health_report/enable
# hwmsg tracepoint (하드웨어 메시지 추적)
echo 1 > /sys/kernel/debug/tracing/events/devlink/devlink_hwmsg/enable
cat /sys/kernel/debug/tracing/trace_pipe
# 출력 예시:
# mlx5_core-1234 [003] .... 12345.678: devlink_hwmsg: bus_name=pci dev_name=0000:03:00.0
# driver_name=mlx5_core incoming=true type=0 buf=... len=64
# trap report tracepoint (trap 이벤트 추적)
echo 1 > /sys/kernel/debug/tracing/events/devlink/devlink_trap_report/enable
# 특정 드라이버의 devlink 이벤트만 필터링
echo 'bus_name == "pci" && dev_name == "0000:03:00.0"' > \
/sys/kernel/debug/tracing/events/devlink/devlink_hwmsg/filter
# perf를 이용한 devlink 이벤트 수집
perf record -e 'devlink:*' -a -- sleep 10
perf script
Netlink 메시지 덤프
devlink CLI와 커널 간의 Netlink 메시지를 직접 관찰하면 ABI 호환성 문제나 파싱 오류를 정확히 진단할 수 있습니다.
# nlmon 커널 모듈을 이용한 Netlink 캡처
modprobe nlmon
ip link add nlmon0 type nlmon
ip link set nlmon0 up
# tcpdump로 Netlink 메시지 캡처
tcpdump -i nlmon0 -w /tmp/devlink_netlink.pcap &
# devlink 명령 실행 (캡처됨)
devlink dev show
devlink port show
# 캡처 중단
kill %1
# Wireshark에서 pcap 파일 분석
# Display filter: genl.family_name == "devlink"
# 정리
ip link del nlmon0
genl.family_name == "devlink" 필터로 devlink 메시지만 선별하면, 각 명령의 TLV 속성 구조를 시각적으로 확인할 수 있습니다. 자동화 도구의 Netlink 메시지가 올바르게 구성되었는지 검증할 때 유용합니다.
devlink core 내부 구조
devlink 코어의 내부 구조를 이해하면 드라이버 개발과 디버깅에 도움이 됩니다. 커널 6.x에서 단일 파일이었던 devlink.c가 기능별로 분리되었고, devl_lock 기반의 새로운 잠금 모델이 도입되었습니다.
주요 내부 구조체
/* 핵심 구조체 관계 (간략화) */
struct devlink {
struct list_head list; /* 전역 devlink 리스트 */
struct list_head port_list; /* 포트 리스트 */
struct list_head reporter_list; /* health reporter 리스트 */
struct list_head param_list; /* 파라미터 리스트 */
struct list_head region_list; /* region 리스트 */
struct list_head rate_list; /* rate 노드 리스트 */
struct list_head linecard_list; /* 라인카드 리스트 */
struct list_head sb_list; /* shared buffer 리스트 */
const struct devlink_ops *ops; /* 드라이버 콜백 */
struct mutex lock; /* devl_lock */
struct device *dev; /* 부모 디바이스 */
u8 reload_failed:1; /* reload 실패 플래그 */
refcount_t refcount; /* 참조 카운트 */
/* ... private data는 이 구조체 뒤에 할당됨 */
};
struct devlink_port {
struct list_head list;
struct list_head region_list;
struct devlink *devlink;
unsigned int index;
struct devlink_port_attrs attrs;
struct net_device *type_dev; /* 연결된 netdev */
struct devlink_rate *devlink_rate; /* 연결된 rate 노드 */
struct devlink_linecard *linecard; /* 소속 라인카드 */
};
struct devlink_health_reporter {
struct list_head list;
struct devlink *devlink;
const struct devlink_health_reporter_ops *ops;
void *priv;
struct devlink_fmsg *dump_fmsg;
u64 graceful_period; /* 밀리초 */
bool auto_recover;
bool auto_dump;
u64 error_count;
u64 recovery_count;
enum devlink_health_reporter_state health_state;
};
struct devlink_trap {
struct list_head list;
const struct devlink_trap_item *trap;
enum devlink_trap_action action;
struct devlink_stats __percpu *stats;
void *priv; /* 드라이버 전용 */
};
struct devlink_rate {
struct list_head list;
enum devlink_rate_type type; /* node or leaf */
struct devlink *devlink;
u64 tx_share; /* 보장 대역폭 */
u64 tx_max; /* 최대 대역폭 */
struct devlink_rate *parent; /* 계층 트리 */
union {
struct devlink_port *devlink_port; /* leaf */
struct {
char *name; /* node 이름 */
refcount_t refcnt;
};
};
};
Netlink 명령 디스패치 흐름
사용자가 devlink dev show를 실행하면, Generic Netlink를 통해 DEVLINK_CMD_GET 명령이 커널에 전달됩니다. 커널의 devlink 코어는 다음과 같은 순서로 처리합니다:
/* Netlink 명령 디스패치 흐름 (간략화) */
/* 1. Generic Netlink 프레임워크가 devlink 패밀리의 명령을 디스패치 */
static const struct genl_small_ops devlink_nl_ops[] = {
{ .cmd = DEVLINK_CMD_GET,
.doit = devlink_nl_cmd_get_doit,
.dumpit = devlink_nl_cmd_get_dumpit, },
{ .cmd = DEVLINK_CMD_PORT_GET,
.doit = devlink_nl_cmd_port_get_doit,
.dumpit = devlink_nl_cmd_port_get_dumpit, },
/* ... 수십 개의 명령 ... */
};
/* 2. doit 콜백에서 devlink 인스턴스 찾기 */
static int devlink_nl_cmd_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
/* bus_name + dev_name으로 devlink 인스턴스 조회 */
struct devlink *dl = devlink_get_from_attrs(genl_info_net(info),
info->attrs);
/* devl_lock 획득 */
devl_lock(dl);
/* 3. ops 콜백 호출 (예: info_get) */
if (dl->ops->info_get)
err = dl->ops->info_get(dl, req, info->extack);
/* 4. 응답 Netlink 메시지 구성 */
devlink_nl_fill(msg, dl, DEVLINK_CMD_GET, ...);
devl_unlock(dl);
return genlmsg_reply(msg, info);
}
devlink_port_register() 대신 devl_port_register()를 사용하세요. 기존 API는 내부적으로 devl_lock을 획득하지만, probe/remove에서 이미 lock을 잡고 있다면 데드락이 발생합니다. 새 API는 호출자가 lock을 잡은 상태를 전제로 합니다.
| 기존 API (deprecated) | 새 API (devl_ 접두사) | 전환 시점 |
|---|---|---|
devlink_port_register() | devl_port_register() | 커널 6.3 |
devlink_port_unregister() | devl_port_unregister() | 커널 6.3 |
devlink_params_register() | devl_params_register() | 커널 6.3 |
devlink_resource_register() | devl_resource_register() | 커널 6.3 |
devlink_health_reporter_create() | devl_health_reporter_create() | 커널 6.3 |
devlink_region_create() | devl_region_create() | 커널 6.3 |
devlink_trap_register() | devl_trap_register() | 커널 6.3 |
devlink_rate_node_create() | devl_rate_node_create() | 커널 6.3 |
flash update 구현 심화
flash update는 NIC의 펌웨어를 원격으로 갱신하는 기능입니다. 수백~수천 대의 장치를 관리하는 데이터센터에서는 표준화된 flash 인터페이스가 운영 효율을 크게 높입니다. devlink flash update는 request_firmware() 프레임워크와 연동하여 펌웨어 이미지를 로드하고, 드라이버별 flash 로직을 실행합니다.
/* flash_update 콜백 구현 예 */
static int my_flash_update(struct devlink *dl,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack)
{
struct my_priv *priv = devlink_priv(dl);
const struct firmware *fw = params->fw;
u32 offset = 0;
int err;
/* component 확인 */
if (params->component &&
strcmp(params->component, "fw.mgmt") != 0) {
NL_SET_ERR_MSG_MOD(extack,
"Unsupported component, use fw.mgmt");
return -EOPNOTSUPP;
}
/* FW 이미지 유효성 검증 */
err = my_validate_fw_image(priv, fw->data, fw->size);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Invalid firmware image");
return err;
}
/* 진행률 보고하며 flash 실행 */
devlink_flash_update_status_notify(dl, "Preparing",
"fw.mgmt", 0, fw->size);
while (offset < fw->size) {
u32 chunk = min((u32)4096, fw->size - offset);
err = my_write_fw_chunk(priv, fw->data + offset, chunk);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Flash write failed");
return err;
}
offset += chunk;
devlink_flash_update_status_notify(dl, "Flashing",
"fw.mgmt", offset, fw->size);
}
devlink_flash_update_status_notify(dl, "Activating",
"fw.mgmt", fw->size, fw->size);
err = my_activate_fw(priv);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "FW activation failed");
return err;
}
devlink_flash_update_status_notify(dl, "Done",
"fw.mgmt", fw->size, fw->size);
return 0;
}
# flash update 운영 절차
# 1. 현재 FW 버전 확인
devlink dev info pci/0000:03:00.0 | grep fw.mgmt
# running: 22.36.1010
# stored: 22.36.1010
# 2. FW 파일을 /lib/firmware 또는 임시 경로에 배치
cp mlx5_fw_22.37.2010.bin /var/tmp/
# 3. 별도 터미널에서 진행률 모니터링
devlink monitor &
# 4. flash 실행
devlink dev flash pci/0000:03:00.0 file /var/tmp/mlx5_fw_22.37.2010.bin
# 5. 결과 확인 (stored 버전만 변경됨)
devlink dev info pci/0000:03:00.0 | grep fw.mgmt
# running: 22.36.1010
# stored: 22.37.2010
# 6. 새 FW 활성화 (재부팅 없이)
devlink dev reload pci/0000:03:00.0 action fw_activate
# 7. 최종 확인
devlink dev info pci/0000:03:00.0 | grep fw.mgmt
# running: 22.37.2010
# stored: 22.37.2010
devlink과 다른 서브시스템 연계
devlink는 독립적으로 동작하지 않고 다양한 커널 서브시스템과 연계됩니다. 각 연계 지점을 이해하면 전체적인 네트워크 스택에서 devlink의 위치를 파악할 수 있습니다.
devlink과 ethtool 정보 비교
# ethtool과 devlink의 정보 비교
# ethtool: 개별 netdev 중심 정보
ethtool -i enp3s0f0
# driver: mlx5_core
# version: 5.15.0
# firmware-version: 22.36.1010 (MT_0000000228)
# bus-info: 0000:03:00.0
# devlink: 장치 전체 정보 (더 상세)
devlink dev info pci/0000:03:00.0
# driver mlx5_core
# serial_number MT2116X09299
# versions:
# fixed: fw.psid, board.id
# running: fw.mgmt, fw.undi, fw.bundle_id
# stored: fw.mgmt, fw.undi
# ethtool: 링크 통계
ethtool -S enp3s0f0 | head -20
# devlink: 장치 레벨 trap 통계
devlink -s trap show pci/0000:03:00.0
devlink과 sysfs 연계
# sysfs를 통한 SR-IOV VF 관리 (devlink 연계)
# VF 생성
echo 4 > /sys/class/net/enp3s0f0/device/sriov_numvfs
# devlink port에 자동 등록 확인
devlink port show pci/0000:03:00.0
# pci/0000:03:00.0/65536: type eth netdev enp3s0f0v0 flavour pci_vf ...
# sysfs: PCI 장치 정보 확인
lspci -vv -s 0000:03:00.0 | grep -i "Physical Slot\|LnkSta\|Region"
# sysfs: NUMA 노드 확인 (성능 최적화)
cat /sys/bus/pci/devices/0000:03:00.0/numa_node
# devlink과 일치하는 netdev 확인
devlink -j port show | jq '.port | to_entries[] | select(.value.netdev != null) | {port: .key, netdev: .value.netdev}'
Ansible을 이용한 대규모 devlink 관리
# Ansible playbook: devlink FW update
---
- name: devlink firmware update
hosts: smartnic_hosts
become: true
serial: 1 # 한 대씩 순차 처리
vars:
devlink_dev: "pci/0000:03:00.0"
fw_file: "/opt/firmware/mlx5_fw_22.37.bin"
min_fw_version: "22.36.0"
tasks:
- name: 사전 스냅샷 수집
shell: |
devlink -j dev info {{ devlink_dev }}
register: dev_info
- name: 현재 FW 버전 확인
set_fact:
current_fw: "{{ (dev_info.stdout | from_json).info
| dict2items | first | community.general.json_query('value.versions.running.\"fw.mgmt\"') }}"
- name: FW 버전 사전 검증
assert:
that:
- current_fw is version(min_fw_version, '>=')
fail_msg: "FW {{ current_fw }}이 최소 버전 미달"
- name: health 상태 확인
shell: |
devlink -j health show {{ devlink_dev }} |
jq '[.[][] | .error] | add'
register: health_errors
failed_when: health_errors.stdout | int > 0
- name: flash update 실행
shell: |
devlink dev flash {{ devlink_dev }} file {{ fw_file }}
timeout: 600
- name: FW 활성화
shell: |
devlink dev reload {{ devlink_dev }} action fw_activate
- name: 사후 검증
shell: |
devlink -j dev info {{ devlink_dev }}
register: post_info
- name: 결과 보고
debug:
msg: "{{ inventory_hostname }}: FW 업데이트 완료"
serial: 1 또는 소규모 배치(serial: 5)로 순차 처리하세요. 동시에 많은 장치를 flash/reload하면 네트워크 전체가 마비될 수 있습니다. 카나리 배포 전략(일부 장치 먼저 업데이트 -> 검증 -> 전체 배포)을 권장합니다.
성능 고려사항
devlink 자체는 제어면 인프라이므로 데이터면 성능에 직접적인 영향은 적습니다. 그러나 설정에 따라 간접적으로 데이터면 성능에 영향을 줄 수 있습니다.
| 설정 항목 | 성능 영향 | 권장 사항 |
|---|---|---|
| trap action=trap | 해당 패킷이 CPU로 전달되어 부하 발생 | policer 설정 필수, 불필요한 trap 최소화 |
| eSwitch mode | switchdev 모드에서 slow path 패킷 증가 가능 | TC/OVS 규칙으로 fast path 오프로드 확보 |
| resource 할당 | FDB/ACL/TCAM 크기가 오프로드 용량 결정 | 워크로드에 맞게 리소스 분배 |
| rate 설정 | tx_share/tx_max가 VF/SF 대역폭 결정 | 과도한 제한 방지, burst 여유 확보 |
| SB pool | pool 크기가 burst 흡수 능력 결정 | tail drop 발생 시 pool 크기 증가 |
| health auto_recover | 복구 중 데이터면 순간 중단 | grace_period를 적절히 설정 |
| reload | 드라이버 재초기화 동안 패킷 손실 | 트래픽 drain 후 실행 |
| SF 수 | 너무 많은 SF는 MSI-X/queue 자원 분산 | 필요한 만큼만 생성, 리소스 모니터링 |
# 성능 관련 devlink 확인 명령
# 1. 현재 resource 사용량 확인
devlink resource show pci/0000:03:00.0
# FDB, ACL, TCAM 용량 확인 -> 오프로드 가능 규칙 수 결정
# 2. shared buffer occupancy 확인
devlink sb occupancy show pci/0000:03:00.0
# pool 점유율이 높으면 tail drop 위험
# 3. trap 통계로 제어면 부하 확인
devlink -s trap show pci/0000:03:00.0
# action=trap인 항목의 패킷 수가 많으면 CPU 부하
# 4. rate 설정 확인 (대역폭 병목 여부)
devlink port function rate show pci/0000:03:00.0
# 5. eSwitch 모드에서 representor 수 확인
devlink port show pci/0000:03:00.0 | grep -c representor
# representor 수가 많으면 slow path 부하 증가 가능
# 6. NUMA 로컬리티 확인 (성능 최적화)
cat /sys/bus/pci/devices/0000:03:00.0/numa_node
# IRQ affinity를 동일 NUMA 노드의 CPU에 설정
devlink trap policer를 설정하여 trap 당 최대 rate/burst를 제한하세요. 제어면 보호가 없으면 관리 접속(SSH) 자체가 불가능해질 수 있습니다.
devlink 발전 역사
devlink 서브시스템은 2015년 Jiri Pirko에 의해 처음 제안되어, 이후 지속적으로 기능이 확장되고 있습니다. 주요 마일스톤을 시간순으로 정리합니다.
| 커널 버전 | 시기 | 추가 기능 | 핵심 변화 |
|---|---|---|---|
| 4.6 | 2016 | devlink 코어 도입 | device/port 기본 구조, switchdev 연계 |
| 4.8 | 2016 | shared buffer (sb) | 혼잡 관리, pool/TC 바인딩 |
| 4.11 | 2017 | eSwitch mode | legacy/switchdev 전환, representor 개념 |
| 4.14 | 2017 | info_get, param | 표준화된 드라이버/FW 정보, 파라미터 |
| 4.18 | 2018 | resource, region | 하드웨어 자원 트리, 메모리 영역 접근 |
| 5.0 | 2019 | health reporter | 구조화된 장애 관리 프레임워크 |
| 5.1 | 2019 | flash update | 표준화된 펌웨어 갱신 인터페이스 |
| 5.3 | 2019 | trap | HW 드롭/예외 패킷 관찰 프레임워크 |
| 5.7 | 2020 | reload action/limit | 세분화된 reload 제어 |
| 5.9 | 2020 | rate | VF/SF별 계층적 대역폭 제어 |
| 5.13 | 2021 | subfunction (SF) | 경량 하드웨어 격리 메커니즘 |
| 5.18 | 2022 | linecard | 모듈형 라인카드 관리 |
| 6.1 | 2022 | devl_lock 도입 | per-instance mutex, 새로운 잠금 모델 |
| 6.3 | 2023 | devl_ 접두사 API | lock-aware API 전환, 기존 API 폐기 시작 |
| 6.5 | 2023 | 소스 파일 분리 | 단일 devlink.c -> 기능별 파일 분리 |
| 6.8+ | 2024 | port function caps | SF migratable, ipsec_crypto 등 확장 |
서브펑션 (SubFunction) 라이프사이클 심화
SF의 전체 라이프사이클은 단순한 생성/삭제를 넘어, 리소스 할당, 큐 구성, IRQ 바인딩, auxiliary 디바이스 프로브, 그리고 운영 중 상태 전환까지 복잡한 단계를 포함합니다. 프로덕션 환경에서 SF를 안정적으로 운영하려면 각 단계의 내부 동작과 실패 시 복구 경로를 정확히 이해해야 합니다.
# SF 라이프사이클 전체 운영 절차 (mlx5 예시)
# 1. SF 생성 (sfnum은 PF 내 고유해야 함)
devlink port add pci/0000:03:00.0 flavour pcisf pfnum 0 sfnum 100
# 출력: pci/0000:03:00.0/32768: type eth ... flavour pcisf ...
# 2. MAC 주소 설정
devlink port function set pci/0000:03:00.0/32768 \
hw_addr 02:00:00:00:00:64
# 3. 기능 플래그 설정 (커널 6.8+)
devlink port function set pci/0000:03:00.0/32768 \
roce true migratable true
# 4. 활성화
devlink port function set pci/0000:03:00.0/32768 state active
# 5. SF devlink 인스턴스 확인
devlink dev show
# auxiliary/mlx5_core.sf.0: ...
# 6. SF의 netdev 확인
devlink port show | grep sf
ip link show | grep "sf\|mlx5"
# 7. SF 독립 모니터링
devlink health show auxiliary/mlx5_core.sf.0
devlink dev info auxiliary/mlx5_core.sf.0
# 8. SF 해제 (역순)
devlink port function set pci/0000:03:00.0/32768 state inactive
# auxiliary 디바이스 제거 대기 (dmesg 확인)
sleep 2
devlink port del pci/0000:03:00.0/32768
| SF 상태 | auxiliary 디바이스 | netdev | devlink 인스턴스 | HW 리소스 |
|---|---|---|---|---|
inactive | 미등록 | 없음 | 없음 | context만 할당 |
active (probe 중) | 등록 중 | 생성 중 | 생성 중 | EQ/CQ/SQ/RQ 할당 |
active (완료) | 등록됨 | 존재 (up/down) | 존재 | 전체 할당 |
inactive (해제 중) | 제거 중 | 제거 중 | 제거 중 | 해제 중 |
cat /sys/bus/pci/devices/0000:03:00.0/msi_irqs | wc -l로 현재 사용 중인 MSI-X 수를 확인하세요. SF 생성 실패 시 가장 먼저 MSI-X 여유분을 확인해야 합니다.
/* SF용 port_new/port_del 콜백 구현 패턴 */
static int my_port_new(struct devlink *dl,
const struct devlink_port_new_attrs *attrs,
struct netlink_ext_ack *extack,
unsigned int *new_port_index)
{
struct my_priv *priv = devlink_priv(dl);
struct my_sf *sf;
int err;
/* flavour 확인 */
if (attrs->flavour != DEVLINK_PORT_FLAVOUR_PCI_SF) {
NL_SET_ERR_MSG_MOD(extack, "Only PCI SF supported");
return -EOPNOTSUPP;
}
/* sfnum 중복 검사 */
if (my_find_sf(priv, attrs->sfnum)) {
NL_SET_ERR_MSG_MOD(extack, "SF number already in use");
return -EEXIST;
}
/* HW context 할당 */
sf = my_sf_alloc(priv, attrs->pfnum, attrs->sfnum);
if (IS_ERR(sf))
return PTR_ERR(sf);
/* devlink 포트 등록 */
err = devl_port_register(dl, &sf->dl_port, sf->port_index);
if (err) {
my_sf_free(sf);
return err;
}
*new_port_index = sf->port_index;
return 0;
}
static int my_port_del(struct devlink *dl,
struct devlink_port *port,
struct netlink_ext_ack *extack)
{
struct my_sf *sf = container_of(port, struct my_sf, dl_port);
/* active 상태면 먼저 비활성화 필요 */
if (sf->active) {
NL_SET_ERR_MSG_MOD(extack, "Deactivate SF first");
return -EBUSY;
}
devl_port_unregister(port);
my_sf_free(sf);
return 0;
}
devlink-trap 그룹과 필터링 심화
대규모 네트워크에서 trap 이벤트는 초당 수만 건 이상 발생할 수 있습니다. 효과적인 trap 관리를 위해서는 그룹 단위 정책 설정, policer 계층 구성, 그리고 메타데이터 기반 필터링이 필수입니다. 이 섹션에서는 실전에서 활용하는 trap 필터링 패턴과 경보 자동화 전략을 다룹니다.
# trap 그룹별 policer 일괄 설정 자동화 스크립트
#!/usr/bin/env bash
DEV="pci/0000:03:00.0"
# 그룹별 policer 정책 정의
declare -A POLICIES=(
["l2_drops"]="1:1000:128"
["l3_drops"]="2:500:64"
["l3_exceptions"]="3:2000:256"
["acl_drops"]="4:100:32"
["buffer_drops"]="5:500:128"
["stp"]="6:100:16"
["ospf"]="7:200:32"
["bgp"]="8:200:32"
)
# policer 설정 적용
for group in "${!POLICIES[@]}"; do
IFS=':' read -r id rate burst <<< "${POLICIES[$group]}"
echo "[INFO] 그룹 $group: policer $id rate=$rate burst=$burst"
devlink trap policer set "$DEV" policer "$id" \
rate "$rate" burst "$burst" 2>/dev/null || true
devlink trap group set "$DEV" group "$group" \
policer "$id" 2>/dev/null || true
done
# 적용 확인
devlink trap group show "$DEV"
# trap 변화율 기반 경보 시스템 (Python)
import json, subprocess, time
class TrapMonitor:
def __init__(self, dev, interval=10):
self.dev = dev
self.interval = interval
self.prev_stats = {}
def get_trap_stats(self):
result = subprocess.run(
["devlink", "-j", "-s", "trap", "show", self.dev],
capture_output=True, text=True
)
if result.returncode != 0:
return {}
data = json.loads(result.stdout)
stats = {}
for trap_info in data.get("trap", {}).values():
for t in (trap_info if isinstance(trap_info, list) else [trap_info]):
name = t.get("name", "unknown")
pkts = t.get("stats", {}).get("rx", {}).get("packets", 0)
drops = t.get("stats", {}).get("drops", {}).get("packets", 0)
stats[name] = {"rx_pkts": pkts, "drop_pkts": drops}
return stats
def check_alerts(self):
curr = self.get_trap_stats()
alerts = []
for name, s in curr.items():
prev = self.prev_stats.get(name, {})
delta_drops = s["drop_pkts"] - prev.get("drop_pkts", 0)
rate = delta_drops / self.interval
if rate > 100: # 초당 100 drops 이상
alerts.append(f"[ALERT] {name}: {rate:.0f} drops/s")
self.prev_stats = curr
return alerts
monitor = TrapMonitor("pci/0000:03:00.0")
while True:
for alert in monitor.check_alerts():
print(alert)
time.sleep(10)
input_port, flow_action_cookie 등의 메타데이터가 포함됩니다. 이 메타데이터를 로그에 함께 기록하면, 어떤 포트/규칙에서 예외가 발생했는지 빠르게 추적할 수 있습니다. devlink trap group set ... action trap으로 그룹 전체를 CPU로 전달하면 tcpdump로 패킷 내용까지 확인할 수 있지만, 반드시 policer와 함께 사용하세요.
헬스 리포터 자동 복구 상세
health reporter의 자동 복구(auto_recover)는 강력한 기능이지만, 잘못 설정하면 복구 루프에 빠져 장치가 불안정해질 수 있습니다. grace_period, recover 횟수 제한, 단계별 에스컬레이션 전략을 올바르게 설계해야 합니다.
# 자동 복구 모니터링과 에스컬레이션
# reporter별 auto_recover/auto_dump 설정
devlink health set pci/0000:03:00.0 reporter fw \
auto_recover true auto_dump true grace_period 60000
devlink health set pci/0000:03:00.0 reporter fw_fatal \
auto_recover true auto_dump true grace_period 0
devlink health set pci/0000:03:00.0 reporter tx \
auto_recover true auto_dump false grace_period 10000
# 복구 이력 확인
devlink health show pci/0000:03:00.0
# reporter fw:
# state healthy error 3 recover 3 grace_period 60000
# auto_recover true auto_dump true
# 복구 루프 감지 스크립트
ERRORS=$(devlink -j health show pci/0000:03:00.0 | \
jq '[.[][] | select(.name == "fw") | .error] | add')
RECOVERS=$(devlink -j health show pci/0000:03:00.0 | \
jq '[.[][] | select(.name == "fw") | .recover] | add')
if [ "$ERRORS" -gt "$((RECOVERS + 2))" ]; then
echo "[ALERT] 복구 실패 누적: errors=$ERRORS, recovers=$RECOVERS"
echo "[ACTION] auto_recover 비활성화 및 운영자 에스컬레이션"
devlink health set pci/0000:03:00.0 reporter fw auto_recover false
fi
| reporter | 권장 grace_period | auto_recover | auto_dump | 에스컬레이션 기준 |
|---|---|---|---|---|
| tx | 10,000ms | true | false | 3회 연속 실패 시 함수 리셋 |
| rx | 10,000ms | true | false | 3회 연속 실패 시 함수 리셋 |
| fw | 60,000ms | true | true | 2회 실패 시 장치 격리 |
| fw_fatal | 0ms | true | true | 1회 실패 시 운영자 호출 |
| pci | 120,000ms | true | true | 1회 발생 시 HW 점검 예약 |
| vnic | 30,000ms | false | true | 진단 전용 (복구 없음) |
error_count - recover_count > 2인 경우 자동으로 auto_recover false를 설정하고 운영자에게 알리는 자동화를 구축하세요. 동시에 devlink health dump show로 최신 덤프를 보존해 벤더에 전달합니다.
플래시 업데이트 프레임워크 심화
대규모 데이터센터에서 수천 대의 NIC 펌웨어를 안전하게 업데이트하려면, 단순한 devlink dev flash 명령 실행을 넘어 체계적인 프레임워크가 필요합니다. 이 섹션에서는 component 기반 부분 업데이트, 진행률 모니터링, 롤백 전략, 그리고 대규모 배포 자동화 패턴을 다룹니다.
| component | 설명 | 업데이트 시 영향 | 활성화 방법 |
|---|---|---|---|
fw.mgmt | 관리 펌웨어 (주 FW) | 장치 기능 전체에 영향 | reload fw_activate 또는 재부팅 |
fw.undi | PXE 부트 ROM | PXE 부팅에만 영향 | 재부팅 시 자동 적용 |
fw.app | 응용 펌웨어 (DPU 등) | 가속 기능에 영향 | component별 상이 |
fw.bundle_id | 전체 FW 번들 | 모든 component 일괄 | reload 또는 재부팅 |
# 대규모 flash update 안전 프레임워크
#!/usr/bin/env bash
set -euo pipefail
DEVICES=(
"pci/0000:03:00.0"
"pci/0000:03:00.1"
"pci/0000:05:00.0"
)
FW_FILE="$1"
EXPECTED_VERSION="$2"
LOG_DIR="/var/log/devlink-flash-$(date +%Y%m%d)"
mkdir -p "$LOG_DIR"
# 사전 검증
pre_check() {
local dev="$1"
local errors
# health 상태 확인
errors=$(devlink -j health show "$dev" | \
jq '[.[][] | .error // 0] | add')
if [ "$errors" -gt 0 ]; then
echo "[FAIL] $dev: health errors=$errors"
return 1
fi
# FW 이미지 무결성 검증
if ! md5sum -c "${FW_FILE}.md5" 2>/dev/null; then
echo "[FAIL] FW 이미지 무결성 검증 실패"
return 1
fi
echo "[PASS] $dev: 사전 검증 통과"
return 0
}
# flash 실행 (진행률 모니터링 포함)
do_flash() {
local dev="$1"
# 모니터링 백그라운드 프로세스
devlink monitor > "$LOG_DIR/${dev//\//_}_monitor.log" 2>&1 &
local mon_pid=$!
echo "[INFO] $dev: flash 시작"
if ! devlink dev flash "$dev" file "$FW_FILE" 2>"$LOG_DIR/${dev//\//_}_error.log"; then
echo "[FAIL] $dev: flash 실패"
kill $mon_pid 2>/dev/null || true
return 1
fi
kill $mon_pid 2>/dev/null || true
echo "[INFO] $dev: flash 완료"
}
# 사후 검증
post_check() {
local dev="$1"
local stored_ver
stored_ver=$(devlink -j dev info "$dev" | \
jq -r '.[][][].versions.stored."fw.mgmt" // "N/A"')
if [ "$stored_ver" != "$EXPECTED_VERSION" ]; then
echo "[FAIL] $dev: stored=$stored_ver, expected=$EXPECTED_VERSION"
return 1
fi
echo "[PASS] $dev: stored 버전 확인 완료 ($stored_ver)"
}
# 메인: 순차 처리
failed=()
for dev in "${DEVICES[@]}"; do
echo "====== $dev ======"
if ! pre_check "$dev"; then
failed+=("$dev")
echo "[SKIP] $dev: 사전 검증 실패로 건너뜀"
continue
fi
if ! do_flash "$dev"; then
failed+=("$dev")
echo "[ABORT] 실패 발생. 잔여 장치 작업 중단."
break
fi
post_check "$dev" || failed+=("$dev")
sleep 5 # 장치 간 안정화 대기
done
echo "====== 결과 ======"
echo "실패: ${#failed[@]}개 (${failed[*]:-없음})"
devlink dev info의 board.id로 모델을 확인하세요.
devlink-rate 트래픽 제어 심화
devlink-rate는 VF/SF별 하드웨어 레벨 대역폭 제어를 계층적으로 구성할 수 있는 메커니즘입니다. tc qdisc와 달리 NIC 하드웨어의 전송 스케줄러에 직접 대역폭 제한을 설정하므로, CPU 오버헤드 없이 정확한 대역폭 격리가 가능합니다.
| 속성 | 설명 | 단위 | 적용 대상 |
|---|---|---|---|
tx_share | 보장 대역폭 (최소) | bit/s | node, leaf |
tx_max | 최대 대역폭 (상한) | bit/s | node, leaf |
tx_priority | 우선순위 (높을수록 우선) | 정수 | node, leaf (일부 드라이버) |
tx_weight | 가중치 (비례 배분) | 정수 | node, leaf (일부 드라이버) |
parent | 부모 노드 이름 | 문자열 | node, leaf |
# rate 트리 구성 실전 예제: 3-tier 테넌트 격리
DEV="pci/0000:03:00.0"
# 1단계: 루트 노드 (물리 링크 대역폭 = 100G)
# 루트 노드는 암시적으로 존재 (설정 불필요)
# 2단계: 테넌트 그룹 노드 생성
devlink port function rate add ${DEV}/premium
devlink port function rate set ${DEV}/premium \
tx_share 50gbit tx_max 80gbit
devlink port function rate add ${DEV}/standard
devlink port function rate set ${DEV}/standard \
tx_share 30gbit tx_max 60gbit
devlink port function rate add ${DEV}/best-effort
devlink port function rate set ${DEV}/best-effort \
tx_share 10gbit tx_max 40gbit
# 3단계: VF/SF를 테넌트 그룹에 배치
# premium 테넌트의 VF들
devlink port function rate set ${DEV}/65536 \
tx_share 10gbit tx_max 25gbit parent premium
devlink port function rate set ${DEV}/65537 \
tx_share 10gbit tx_max 25gbit parent premium
# standard 테넌트의 SF들
devlink port function rate set ${DEV}/32768 \
tx_share 5gbit tx_max 15gbit parent standard
devlink port function rate set ${DEV}/32769 \
tx_share 5gbit tx_max 15gbit parent standard
# best-effort 테넌트
devlink port function rate set ${DEV}/65540 \
tx_share 2gbit tx_max 10gbit parent best-effort
# 4단계: rate 트리 확인
devlink port function rate show ${DEV}
# 출력:
# pci/0000:03:00.0/premium: type node tx_share 50gbit tx_max 80gbit
# pci/0000:03:00.0/standard: type node tx_share 30gbit tx_max 60gbit
# pci/0000:03:00.0/best-effort: type node tx_share 10gbit tx_max 40gbit
# pci/0000:03:00.0/65536: type leaf tx_share 10gbit tx_max 25gbit parent premium
# ...
# 5단계: rate 노드 삭제 (리프 먼저 해제)
devlink port function rate set ${DEV}/65540 noparent
devlink port function rate del ${DEV}/best-effort
tx_share 합산이 부모 노드의 tx_max를 초과하지 않도록 설계해야 합니다. 초과하면 보장 대역폭을 모두 충족할 수 없는 상황이 발생합니다. 또한 tx_share는 트래픽이 있을 때만 의미가 있으며, 유휴 대역폭은 다른 노드에 자동으로 재분배됩니다.
실제 드라이버 구현 예제 (mlx5, ice)
mlx5와 ice는 가장 많이 사용되는 devlink 지원 드라이버입니다. 두 드라이버의 구현 패턴을 비교하면 devlink 콜백을 어떻게 구현해야 하는지 실무적인 감을 잡을 수 있습니다.
mlx5 devlink 초기화 흐름
/* drivers/net/ethernet/mellanox/mlx5/core/devlink.c */
/* mlx5 devlink ops 정의 (주요 콜백) */
static const struct devlink_ops mlx5_devlink_ops = {
.supported_flash_update_params =
DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT |
DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
.reload_limits = BIT(DEVLINK_RELOAD_LIMIT_NO_RESET),
.reload_down = mlx5_devlink_reload_down,
.reload_up = mlx5_devlink_reload_up,
.flash_update = mlx5_devlink_flash_update,
.info_get = mlx5_devlink_info_get,
.trap_init = mlx5_devlink_trap_init,
.trap_fini = mlx5_devlink_trap_fini,
.trap_action_set = mlx5_devlink_trap_action_set,
.port_new = mlx5_devlink_sf_port_new,
.port_del = mlx5_devlink_sf_port_del,
.port_fn_hw_addr_get = mlx5_devlink_port_fn_hw_addr_get,
.port_fn_hw_addr_set = mlx5_devlink_port_fn_hw_addr_set,
.port_fn_state_get = mlx5_devlink_sf_port_fn_state_get,
.port_fn_state_set = mlx5_devlink_sf_port_fn_state_set,
.rate_leaf_tx_share_set = mlx5_esw_devlink_rate_leaf_tx_share_set,
.rate_leaf_tx_max_set = mlx5_esw_devlink_rate_leaf_tx_max_set,
.rate_node_tx_share_set = mlx5_esw_devlink_rate_node_tx_share_set,
.rate_node_tx_max_set = mlx5_esw_devlink_rate_node_tx_max_set,
.rate_node_new = mlx5_esw_devlink_rate_node_new,
.rate_node_del = mlx5_esw_devlink_rate_node_del,
};
/* mlx5 probe에서 devlink 초기화 */
static int mlx5_init_one(struct mlx5_core_dev *dev)
{
struct devlink *devlink = priv_to_devlink(dev);
/* param 등록 */
mlx5_devlink_params_register(devlink);
/* health reporter 생성 */
mlx5_fw_reporter_create(dev); /* fw reporter */
mlx5_fw_fatal_reporter_create(dev); /* fw_fatal reporter */
/* eSwitch 설정 (switchdev 지원) */
mlx5_eswitch_init(dev);
/* devlink 등록 (Netlink 노출) */
devlink_register(devlink);
return 0;
}
ice devlink 초기화 흐름
/* drivers/net/ethernet/intel/ice/ice_devlink.c */
/* ice devlink ops 정의 */
static const struct devlink_ops ice_devlink_ops = {
.supported_flash_update_params =
DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
.reload_down = ice_devlink_reload_down,
.reload_up = ice_devlink_reload_up,
.flash_update = ice_devlink_flash_update,
.info_get = ice_devlink_info_get,
.port_split = ice_devlink_port_split,
.port_unsplit = ice_devlink_port_unsplit,
.eswitch_mode_get = ice_eswitch_mode_get,
.eswitch_mode_set = ice_eswitch_mode_set,
};
/* ice에서의 region 등록 예 */
static void ice_devlink_init_regions(struct ice_pf *pf)
{
struct devlink *devlink = priv_to_devlink(pf);
/* NVM flash 영역 */
pf->nvm_region = devl_region_create(devlink,
&ice_nvm_region_ops, 1, pf->hw.flash.size);
/* Shadow RAM 영역 */
pf->sram_region = devl_region_create(devlink,
&ice_sram_region_ops, 1, pf->hw.flash.sr_size);
/* Device Capabilities 영역 */
pf->devcaps_region = devl_region_create(devlink,
&ice_devcaps_region_ops, 10,
ICE_AQ_MAX_BUF_LEN);
}
info_get과 flash_update를 구현하세요. 이 두 콜백만으로도 자산 관리와 펌웨어 갱신이라는 핵심 가치를 제공할 수 있습니다. 이후 필요에 따라 health reporter, trap, rate 등을 점진적으로 추가하면 됩니다.
devlink 리로드 메커니즘 심화
devlink reload은 장치를 재부팅하지 않고 드라이버를 재초기화하거나 새 펌웨어를 활성화하는 메커니즘입니다. action과 limit 조합에 따라 영향 범위가 크게 달라지므로, 각 조합의 동작을 정확히 이해해야 합니다.
| action | limit | 데이터면 중단 | 주요 용도 | 지원 드라이버 |
|---|---|---|---|---|
driver_reinit | 없음 | 전체 중단 | driverinit param 적용, resource 재분배 | mlx5, ice, bnxt |
driver_reinit | no_reset | 최소 중단 | 리셋 없이 드라이버 재초기화 | mlx5 (제한적) |
fw_activate | 없음 | 전체 중단 | flash 후 새 FW 적용 | mlx5 |
fw_activate | no_reset | 최소 중단 | 리셋 없이 FW 활성화 (지원 시) | mlx5 (일부 FW) |
# reload 안전 운영 절차
# 1. 지원 범위 확인 (드라이버 capabilities)
devlink dev info pci/0000:03:00.0
# reload_actions: driver_reinit fw_activate
# reload_limits: no_reset
# 2. driverinit param 변경 (reload 전)
devlink dev param set pci/0000:03:00.0 \
name enable_roce value false cmode driverinit
# 3. 사전 상태 저장
devlink -j port show pci/0000:03:00.0 > /tmp/ports_before.json
devlink -j health show pci/0000:03:00.0 > /tmp/health_before.json
# 4. reload 실행
devlink dev reload pci/0000:03:00.0 action driver_reinit
# 5. 사후 검증
devlink -j port show pci/0000:03:00.0 > /tmp/ports_after.json
diff <(jq '.port | keys' /tmp/ports_before.json) \
<(jq '.port | keys' /tmp/ports_after.json)
# 6. param 적용 확인
devlink dev param show pci/0000:03:00.0 name enable_roce
# cmode driverinit value false (현재 적용됨)
# no_reset limit 사용 (데이터면 중단 최소화)
devlink dev reload pci/0000:03:00.0 \
action fw_activate limit no_reset 2>/dev/null || \
devlink dev reload pci/0000:03:00.0 action fw_activate
# no_reset 실패 시 일반 reload로 폴백
devl_lock이 잡혀 있으므로, 다른 devlink 명령(port show, health show 등)이 블로킹됩니다. 모니터링 스크립트가 reload 중에 devlink 명령을 호출하면 타임아웃이 발생할 수 있습니다. reload 전에 모니터링 간격을 늘리거나, 타임아웃 처리를 추가하세요.
linecard 관리 심화
linecard는 모듈형 섀시 스위치에서 교체 가능한 네트워크 모듈을 관리하는 인터페이스입니다. Mellanox Spectrum 시리즈 스위치(mlxsw 드라이버)에서 주로 사용되며, 라인카드의 핫 플러그, 타입 프로비저닝, 포트 매핑을 제어합니다.
| 상태 | 설명 | 포트 노출 | 트래픽 전달 |
|---|---|---|---|
unprovisioned | 슬롯 비어있거나 타입 미설정 | 없음 | 불가 |
provisioning | 타입 설정 적용 중 | 준비 중 | 불가 |
provisioned | 타입 설정 완료, 카드 미삽입 | 있음 (비활성) | 불가 |
active | 물리 카드 삽입됨, 동작 중 | 있음 (활성) | 가능 |
provisioning_failed | 프로비저닝 오류 | 없음 | 불가 |
# linecard 운영 시나리오
# 1. 지원 라인카드 타입 확인
devlink lc show pci/0000:03:00.0
# pci/0000:03:00.0:
# lc 1 state unprovisioned
# supported_types: 16x100G 32x50G 4x400G 8x200G
# 2. 사전 프로비저닝 (카드 삽입 전 포트 설정 준비)
devlink lc set pci/0000:03:00.0 lc 1 type 16x100G
# 3. 프로비저닝 상태 확인
devlink lc show pci/0000:03:00.0 lc 1
# lc 1 state provisioned type 16x100G
# 4. 포트 확인 (프로비저닝 후 포트가 미리 노출됨)
devlink port show pci/0000:03:00.0 | grep "lc 1"
# 5. 물리 카드 삽입 후 -> 자동으로 active 전환
devlink monitor # lc state 변경 이벤트 수신
# 6. 프로비저닝 해제
devlink lc set pci/0000:03:00.0 lc 1 notype
# 7. 실패 시 복구
# provisioning_failed 상태면 notype 후 재설정
devlink lc set pci/0000:03:00.0 lc 1 notype
devlink lc set pci/0000:03:00.0 lc 1 type 16x100G
active 상태로 전환되면 즉시 트래픽을 전달할 수 있으므로, 네트워크 확장 시 다운타임을 최소화할 수 있습니다.
성능 모니터링과 진단
devlink는 제어면 인프라이지만, 적절한 모니터링 구성을 통해 데이터면의 성능 문제를 간접적으로 탐지할 수 있습니다. trap 통계, health 지표, shared buffer 점유율, resource 사용량을 종합하면 네트워크 장비의 전반적인 건강 상태를 파악할 수 있습니다.
# Prometheus exporter 형태의 devlink 메트릭 수집기
import json, subprocess, time, sys
class DevlinkMetrics:
"""devlink 메트릭 수집기 (Prometheus textfile collector 호환)"""
def __init__(self, dev):
self.dev = dev
self.metrics = []
def _cmd(self, *args):
try:
r = subprocess.run(
["devlink", "-j"] + list(args),
capture_output=True, text=True, timeout=5
)
return json.loads(r.stdout) if r.returncode == 0 else {}
except:
return {}
def collect_health(self):
data = self._cmd("health", "show", self.dev)
for items in data.get("health", {}).values():
for r in (items if isinstance(items, list) else [items]):
name = r.get("name", "unknown")
self.metrics.append(
f'devlink_health_errors{{dev="{self.dev}",reporter="{name}"}} {r.get("error", 0)}')
self.metrics.append(
f'devlink_health_recovers{{dev="{self.dev}",reporter="{name}"}} {r.get("recover", 0)}')
def collect_trap_stats(self):
data = self._cmd("-s", "trap", "show", self.dev)
for items in data.get("trap", {}).values():
for t in (items if isinstance(items, list) else [items]):
name = t.get("name", "unknown")
drops = t.get("stats", {}).get("drops", {}).get("packets", 0)
self.metrics.append(
f'devlink_trap_drops_total{{dev="{self.dev}",trap="{name}"}} {drops}')
def collect_port_count(self):
data = self._cmd("port", "show", self.dev)
count = len(data.get("port", {}))
self.metrics.append(
f'devlink_port_count{{dev="{self.dev}"}} {count}')
def output(self, path="/var/lib/prometheus/devlink.prom"):
self.metrics = []
self.collect_health()
self.collect_trap_stats()
self.collect_port_count()
with open(path, "w") as f:
f.write("\n".join(self.metrics) + "\n")
# 30초 간격 수집
m = DevlinkMetrics("pci/0000:03:00.0")
while True:
m.output()
time.sleep(30)
| 메트릭 | 레이블 | 경보 기준 | 조치 |
|---|---|---|---|
devlink_health_errors | dev, reporter | delta > 0 (5분간) | reporter별 진단/복구 |
devlink_trap_drops_total | dev, trap | rate > 100/s | trap 원인 분석, policer 조정 |
devlink_sb_occupancy_pct | dev, pool | > 80% | pool 크기 증가 또는 트래픽 분산 |
devlink_resource_usage_pct | dev, resource | > 90% | resource 재분배 (reload 필요) |
devlink_port_count | dev | 예상값과 불일치 | 포트 토폴로지 검증 |
참고자료
- 커널 공식 문서: devlink
- 커널 문서: devlink-port
- 커널 문서: devlink-trap
- 커널 문서: devlink-health
- 커널 문서: devlink-params
- 커널 문서: devlink-region
- 커널 문서: devlink-resource
- 커널 문서: representors
- 커널 소스: net/devlink
- 커널 소스: mlx5 devlink 구현
- 커널 소스: ice devlink 구현
- man 페이지: devlink(8)
- man 페이지: devlink-dev(8)
- man 페이지: devlink-port(8)
- man 페이지: devlink-health(8)