# net_device: 등록한 인터페이스를 실제로 켜는 절차

v6.18.37 / net/core/dev.c

네트워크 인터페이스가 목록에 보이는 것과 패킷을 주고받을 준비가 된 것은 다릅니다. net_device는 장치의 공통 상태와 드라이버 연산을 연결합니다. __dev_open 전체를 따라가면 부모 장치의 전원 상태, 주소 검사, 드라이버 시작 함수, 큐 활성화가 서로 다른 단계라는 점을 알 수 있습니다.

## __dev_open

```c

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;
}

```

### 1660행

```c

static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)

```

공통 네트워크 장치를 여는 내부 함수입니다. extack은 자세한 오류 설명을 전달하는 용도로 쓰입니다.

### 1662행

```c

	const struct net_device_ops *ops = dev->netdev_ops;

```

장치별 드라이버 연산 표를 얻습니다.

### 1663행

```c

	int ret;

```

사전 알림과 드라이버 콜백의 결과를 저장합니다.

### 1665행

```c

	ASSERT_RTNL();

```

필요한 RTNL 보호 아래 호출됐는지 검증합니다. 이 줄이 잠금을 새로 잡는 것은 아닙니다.

### 1666행

```c

	dev_addr_check(dev);

```

장치 주소 관리 상태를 점검합니다.

### 1668행

```c

	if (!netif_device_present(dev)) {

```

장치가 현재 사용 가능한 상태로 연결되어 있는지 확인합니다.

### 1670행

```c

		if (dev->dev.parent)

```

장치를 깨울 수 있는 부모 객체가 있는지 확인합니다.

### 1671행

```c

			pm_runtime_resume(dev->dev.parent);

```

부모의 runtime PM 정지가 원인이었을 가능성을 고려해 재개를 요청합니다.

### 1672행

```c

		if (!netif_device_present(dev))

```

부모 재개 후에도 장치가 사용 불가능한지 다시 확인합니다.

### 1673행

```c

			return -ENODEV;

```

사용할 장치가 없다고 -ENODEV를 반환합니다.

### 1680행

```c

	netpoll_poll_disable(dev);

```

장치를 여는 동안 netpoll이 불완전한 드라이버 상태를 사용하지 못하도록 조정합니다.

### 1682행

```c

	ret = call_netdevice_notifiers_extack(NETDEV_PRE_UP, dev, extack);

```

NETDEV_PRE_UP 사전 알림을 보내 관련 기능이 장치 열기를 검사하게 합니다.

### 1683행

```c

	ret = notifier_to_errno(ret);

```

notifier 형식 반환값을 일반 음수 오류 형식으로 바꿉니다.

### 1684행

```c

	if (ret)

```

사전 알림에서 거부됐는지 검사합니다.

### 1685행

```c

		return ret;

```

드라이버를 열지 않고 해당 오류를 반환합니다. 이 조기 반환과 아래 정상 정리 경로를 구분해 읽어야 합니다.

### 1687행

```c

	set_bit(__LINK_STATE_START, &dev->state);

```

장치를 시작하는 상태 비트를 켭니다. 이 비트만으로 open 성공이 확정되는 것은 아닙니다.

### 1689행

```c

	netdev_ops_assert_locked(dev);

```

드라이버 연산 호출에 필요한 잠금 상태를 검증합니다.

### 1691행

```c

	if (ops->ndo_validate_addr)

```

드라이버가 주소 검증 함수를 제공하는지 확인합니다.

### 1692행

```c

		ret = ops->ndo_validate_addr(dev);

```

MAC 주소 등 장치 주소가 허용되는지 드라이버에 묻습니다.

### 1694행

```c

	if (!ret && ops->ndo_open)

```

앞 검사에 성공했고 드라이버 open 함수도 있을 때 실행합니다.

### 1695행

```c

		ret = ops->ndo_open(dev);

```

드라이버에 장치를 사용할 준비를 하도록 요청합니다. 구체적 DMA·인터럽트 설정은 해당 드라이버 구현에 있습니다.

### 1697행

```c

	netpoll_poll_enable(dev);

```

정상 진행 경로에서 netpoll 폴링을 다시 허용합니다.

### 1699행

```c

	if (ret)

```

주소 검사 또는 드라이버 open에 실패했는지 검사합니다.

### 1700행

```c

		clear_bit(__LINK_STATE_START, &dev->state);

```

실패했으므로 앞서 켠 시작 상태 비트를 되돌립니다.

### 1701행

```c

	else {

```

장치 열기에 성공한 경우의 후처리입니다.

### 1702행

```c

		netif_set_up(dev, true);

```

공통 장치 상태를 UP으로 설정합니다.

### 1703행

```c

		dev_set_rx_mode(dev);

```

멀티캐스트 등 현재 설정에 맞는 수신 모드를 적용합니다.

### 1704행

```c

		dev_activate(dev);

```

장치의 패킷 처리 큐를 활성화합니다.

### 1705행

```c

		add_device_randomness(dev->dev_addr, dev->addr_len);

```

장치 주소 정보를 난수 초기화 재료에 더합니다. 주소 자체가 강한 비밀값이라는 뜻은 아닙니다.

### 1708행

```c

	return ret;

```

열기 성공 또는 실패 코드를 반환합니다.

