이 코드는 어떤 문제를 푸나요?
네트워크 인터페이스가 목록에 보이는 것과 패킷을 주고받을 준비가 된 것은 다릅니다. net_device는 장치의 공통 상태와 드라이버 연산을 연결합니다. __dev_open 전체를 따라가면 부모 장치의 전원 상태, 주소 검사, 드라이버 시작 함수, 큐 활성화가 서로 다른 단계라는 점을 알 수 있습니다.
읽을 범위: v6.18.37 · net/core/dev.c · __dev_open 1660–1709행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
등록과 UP
등록은 네트워크 장치를 커널이 관리하게 만드는 일이고 UP은 사용하도록 여는 상태입니다. UP이라고 케이블 연결이나 원격 통신 성공까지 보장되지는 않습니다.
net_device_ops
공통 네트워크 계층이 하드웨어 드라이버의 ndo_open·ndo_start_xmit 등을 호출하기 위한 함수 표입니다.
RTNL
인터페이스 구성 변경을 서로 조정하는 네트워크 잠금입니다. 이 내부 함수는 호출자가 적절한 잠금 조건을 마련했음을 요구합니다.
netpoll
특수 상황에서 네트워크를 폴링하는 경로입니다. 장치를 여는 도중 불완전한 상태의 드라이버를 건드리지 않도록 조정합니다.
처음 읽을 때
등록, 관리상 UP, 실제 링크 연결을 세 가지로 나누어 보십시오. __dev_open의 성공은 주로 장치를 사용하도록 준비한 상태를 뜻하며 패킷이 상대방까지 도착했다는 뜻은 아닙니다.
더 깊이 살펴볼 때
드라이버 콜백의 잠금 조건과 NETDEV_PRE_UP 알림 실패, ndo_open 실패를 구분하십시오. 이 함수의 각 오류 반환이 어떤 상태 변경 이전 또는 이후에 발생하는지 보면 상위 호출자의 책임도 드러납니다.
그림으로 보는 변화

1. 장치와 전원을 확인합니다
장치 존재 여부를 확인하고 필요한 경우 부모 장치를 runtime resume합니다.
화살표는 자식 인터페이스에서 부모 장치 의존성을 확인하는 방향입니다.
2. 드라이버를 엽니다
사전 알림과 주소 검사를 거쳐 ndo_open을 실행합니다.
화살표는 공통 계층에서 드라이버 콜백으로 넘어가는 호출입니다.
3. 인터페이스를 활성화합니다
성공하면 UP 상태, 수신 모드와 전송 큐 등을 설정합니다. 실패하면 시작 표시를 정리합니다.
분기 화살표는 준비 성공과 실패이며 물리 링크 상태를 직접 표현하지 않습니다.
__dev_open를 한 줄씩 읽기
줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)
{
const struct net_device_ops *ops = dev->netdev_ops;
int ret;
ASSERT_RTNL();
dev_addr_check(dev);
if (!netif_device_present(dev)) {
/* may be detached because parent is runtime-suspended */
if (dev->dev.parent)
pm_runtime_resume(dev->dev.parent);
if (!netif_device_present(dev))
return -ENODEV;
}
/* Block netpoll from trying to do any rx path servicing.
* If we don't do this there is a chance ndo_poll_controller
* or ndo_poll may be running while we open the device
*/
netpoll_poll_disable(dev);
ret = call_netdevice_notifiers_extack(NETDEV_PRE_UP, dev, extack);
ret = notifier_to_errno(ret);
if (ret)
return ret;
set_bit(__LINK_STATE_START, &dev->state);
netdev_ops_assert_locked(dev);
if (ops->ndo_validate_addr)
ret = ops->ndo_validate_addr(dev);
if (!ret && ops->ndo_open)
ret = ops->ndo_open(dev);
netpoll_poll_enable(dev);
if (ret)
clear_bit(__LINK_STATE_START, &dev->state);
else {
netif_set_up(dev, true);
dev_set_rx_mode(dev);
dev_activate(dev);
add_device_randomness(dev->dev_addr, dev->addr_len);
}
return ret;
}static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)공통 네트워크 장치를 여는 내부 함수입니다. extack은 자세한 오류 설명을 전달하는 용도로 쓰입니다.
const struct net_device_ops *ops = dev->netdev_ops;장치별 드라이버 연산 표를 얻습니다.
int ret;사전 알림과 드라이버 콜백의 결과를 저장합니다.
ASSERT_RTNL();필요한 RTNL 보호 아래 호출됐는지 검증합니다. 이 줄이 잠금을 새로 잡는 것은 아닙니다.
dev_addr_check(dev);장치 주소 관리 상태를 점검합니다.
if (!netif_device_present(dev)) {장치가 현재 사용 가능한 상태로 연결되어 있는지 확인합니다.
if (dev->dev.parent)장치를 깨울 수 있는 부모 객체가 있는지 확인합니다.
pm_runtime_resume(dev->dev.parent);부모의 runtime PM 정지가 원인이었을 가능성을 고려해 재개를 요청합니다.
if (!netif_device_present(dev))부모 재개 후에도 장치가 사용 불가능한지 다시 확인합니다.
return -ENODEV;사용할 장치가 없다고 -ENODEV를 반환합니다.
netpoll_poll_disable(dev);장치를 여는 동안 netpoll이 불완전한 드라이버 상태를 사용하지 못하도록 조정합니다.
ret = call_netdevice_notifiers_extack(NETDEV_PRE_UP, dev, extack);NETDEV_PRE_UP 사전 알림을 보내 관련 기능이 장치 열기를 검사하게 합니다.
ret = notifier_to_errno(ret);notifier 형식 반환값을 일반 음수 오류 형식으로 바꿉니다.
if (ret)사전 알림에서 거부됐는지 검사합니다.
return ret;드라이버를 열지 않고 해당 오류를 반환합니다. 이 조기 반환과 아래 정상 정리 경로를 구분해 읽어야 합니다.
set_bit(__LINK_STATE_START, &dev->state);장치를 시작하는 상태 비트를 켭니다. 이 비트만으로 open 성공이 확정되는 것은 아닙니다.
netdev_ops_assert_locked(dev);드라이버 연산 호출에 필요한 잠금 상태를 검증합니다.
if (ops->ndo_validate_addr)드라이버가 주소 검증 함수를 제공하는지 확인합니다.
ret = ops->ndo_validate_addr(dev);MAC 주소 등 장치 주소가 허용되는지 드라이버에 묻습니다.
if (!ret && ops->ndo_open)앞 검사에 성공했고 드라이버 open 함수도 있을 때 실행합니다.
ret = ops->ndo_open(dev);드라이버에 장치를 사용할 준비를 하도록 요청합니다. 구체적 DMA·인터럽트 설정은 해당 드라이버 구현에 있습니다.
netpoll_poll_enable(dev);정상 진행 경로에서 netpoll 폴링을 다시 허용합니다.
if (ret)주소 검사 또는 드라이버 open에 실패했는지 검사합니다.
clear_bit(__LINK_STATE_START, &dev->state);실패했으므로 앞서 켠 시작 상태 비트를 되돌립니다.
else {장치 열기에 성공한 경우의 후처리입니다.
netif_set_up(dev, true);공통 장치 상태를 UP으로 설정합니다.
dev_set_rx_mode(dev);멀티캐스트 등 현재 설정에 맞는 수신 모드를 적용합니다.
dev_activate(dev);장치의 패킷 처리 큐를 활성화합니다.
add_device_randomness(dev->dev_addr, dev->addr_len);장치 주소 정보를 난수 초기화 재료에 더합니다. 주소 자체가 강한 비밀값이라는 뜻은 아닙니다.
return ret;열기 성공 또는 실패 코드를 반환합니다.
함께 생각해 볼 질문
ip link에서 보이면 하드웨어가 이미 켜졌나요?
등록된 상태일 수 있습니다. 실제 사용을 위한 open과 전원·큐 활성화는 별개입니다.
UP이면 인터넷 연결도 된 것인가요?
아닙니다. 링크, 주소, 라우팅, 상대방 상태 등 추가 조건이 필요합니다.
부모 장치를 깨우는 이유는 무엇인가요?
버스나 상위 장치가 정지돼 있으면 자식 NIC의 레지스터와 기능에 접근할 수 없는 경우가 있기 때문입니다.
출처와 읽은 범위
Linux stable v6.18.37 · net/core/dev.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
