이 코드는 어떤 문제를 푸나요?
네트워크 인터페이스가 목록에 보이는 것과 패킷을 주고받을 준비가 된 것은 다릅니다. net_device는 장치의 공통 상태와 드라이버 연산을 연결합니다. __dev_open 전체를 따라가면 부모 장치의 전원 상태, 주소 검사, 드라이버 시작 함수, 큐 활성화가 서로 다른 단계라는 점을 알 수 있습니다.
읽을 범위: v6.6 · net/core/dev.c · __dev_open 1442–1489행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
등록과 UP
등록은 네트워크 장치를 커널이 관리하게 만드는 일이고 UP은 사용하도록 여는 상태입니다. UP이라고 케이블 연결이나 원격 통신 성공까지 보장되지는 않습니다.
net_device_ops
공통 네트워크 계층이 하드웨어 드라이버의 ndo_open·ndo_start_xmit 등을 호출하기 위한 함수 표입니다.
RTNL
인터페이스 구성 변경을 서로 조정하는 네트워크 잠금입니다. 이 내부 함수는 호출자가 적절한 잠금 조건을 마련했음을 요구합니다.
netpoll
특수 상황에서 네트워크를 폴링하는 경로입니다. 장치를 여는 도중 불완전한 상태의 드라이버를 건드리지 않도록 조정합니다.
처음 읽을 때
등록, 관리상 UP, 실제 링크 연결을 세 가지로 나누어 보십시오. __dev_open의 성공은 주로 장치를 사용하도록 준비한 상태를 뜻하며 패킷이 상대방까지 도착했다는 뜻은 아닙니다.
더 깊이 살펴볼 때
드라이버 콜백의 잠금 조건과 NETDEV_PRE_UP 알림 실패, ndo_open 실패를 구분하십시오. 이 함수의 각 오류 반환이 어떤 상태 변경 이전 또는 이후에 발생하는지 보면 상위 호출자의 책임도 드러납니다.
열기 결과를 가르는 분기 그림
__dev_open()은 장치가 계속 없거나 NETDEV_PRE_UP 통지가 오류를 돌려주면 START 설정 전에 끝납니다. 그 뒤 선택적인 주소 검증 또는 ndo_open이 실패하면 netpoll_poll_enable()을 거쳐 START를 지우고 오류를 반환합니다. ret == 0일 때만 UP 상태 설정과 dev_activate()로 진행합니다.
__dev_open(). 그림 크게 보기. v6.6은 dev->flags |= IFF_UP, v6.18.37은 netif_set_up(dev, true)를 호출합니다. v6.18.37의 netdev_ops_assert_locked(dev)는 START 설정 뒤 추가되며 그림의 반환 경로를 바꾸지 않습니다.| 조건 | 반환 | 이 함수의 START·UP 처리 |
|---|---|---|
| resume 후에도 장치 부재 | -ENODEV | START 설정 전 |
NETDEV_PRE_UP 오류 | 변환한 ret | START 설정 전 |
| 주소 검증 실패 | 검증 ret | ndo_open 생략, START 해제 |
ndo_open 실패 | open ret | START 해제 |
| 성공 | 0 | 버전별 UP 설정, dev_activate() |
__dev_open를 한 줄씩 읽기
줄 번호는 v6.6 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
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);
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 {
dev->flags |= IFF_UP;
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 성공이 확정되는 것은 아닙니다.
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 {장치 열기에 성공한 경우의 후처리입니다.
dev->flags |= IFF_UP;드라이버 열기에 성공했으므로 장치 플래그에 IFF_UP을 직접 설정합니다. 이 v6.6 본문에는 netif_set_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.6 · net/core/dev.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고