# 송신 경로: 드라이버가 패킷을 받지 못했을 때 목록을 지키는 방법

v6.18.37 / net/core/dev.c

소켓에서 만든 패킷은 라우팅과 qdisc 등을 거쳐 NIC 드라이버로 내려갑니다. 드라이버의 송신 큐가 꽉 차면 남은 패킷을 잃지 않고 나중에 다시 처리할 수 있어야 합니다. dev_hard_start_xmit 전체는 연결된 skb 목록을 하나씩 넘기면서 누가 각 패킷을 소유하는지 정리하는 경계입니다.

## dev_hard_start_xmit

```c

struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
				    struct netdev_queue *txq, int *ret)
{
	struct sk_buff *skb = first;
	int rc = NETDEV_TX_OK;

	while (skb) {
		struct sk_buff *next = skb->next;

		skb_mark_not_on_list(skb);
		rc = xmit_one(skb, dev, txq, next != NULL);
		if (unlikely(!dev_xmit_complete(rc))) {
			skb->next = next;
			goto out;
		}

		skb = next;
		if (netif_tx_queue_stopped(txq) && skb) {
			rc = NETDEV_TX_BUSY;
			break;
		}
	}

out:
	*ret = rc;
	return skb;
}

```

### 3882행

```c

struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,

```

송신할 skb 목록의 첫 항목과 대상 장치를 받습니다.

### 3883행

```c

				    struct netdev_queue *txq, int *ret)

```

대상 송신 큐와 상태를 돌려줄 정수 포인터를 추가로 받습니다. 함수 반환 포인터는 남은 패킷을 나타냅니다.

### 3885행

```c

	struct sk_buff *skb = first;

```

현재 처리할 skb를 목록의 시작으로 둡니다.

### 3886행

```c

	int rc = NETDEV_TX_OK;

```

아직 실패가 없으므로 전송 접수 상태를 NETDEV_TX_OK로 준비합니다.

### 3888행

```c

	while (skb) {

```

미처리 skb가 있는 동안 하나씩 처리합니다.

### 3889행

```c

		struct sk_buff *next = skb->next;

```

드라이버가 현재 skb를 소비할 수 있으므로 다음 포인터를 호출 전에 저장합니다.

### 3891행

```c

		skb_mark_not_on_list(skb);

```

현재 skb를 목록에 연결된 상태로 취급하지 않도록 표시를 정리합니다.

### 3892행

```c

		rc = xmit_one(skb, dev, txq, next != NULL);

```

현재 패킷을 하나 전송합니다. 다음 패킷 유무도 넘겨 연속 발행 최적화에 사용할 수 있게 합니다.

### 3893행

```c

		if (unlikely(!dev_xmit_complete(rc))) {

```

드라이버가 패킷을 접수해 소유권 처리가 끝난 결과인지 검사합니다. 실제 선로 전송 완료 검사와는 다릅니다.

### 3894행

```c

			skb->next = next;

```

접수되지 않은 패킷이면 다음 링크를 복원해 남은 목록이 끊어지지 않게 합니다.

### 3895행

```c

			goto out;

```

현재 패킷부터 다시 처리할 수 있도록 반환 절차로 갑니다.

### 3898행

```c

		skb = next;

```

현재 패킷 처리가 끝났으므로 미리 저장한 다음 skb로 이동합니다.

### 3899행

```c

		if (netif_tx_queue_stopped(txq) && skb) {

```

큐가 정지했고 아직 남은 패킷도 있는지 함께 확인합니다.

### 3900행

```c

			rc = NETDEV_TX_BUSY;

```

뒤의 패킷들을 지금 더 보낼 수 없다는 BUSY 상태를 설정합니다.

### 3901행

```c

			break;

```

남은 목록을 유지한 채 순회를 끝냅니다.

### 3905행

```c

out:

```

정상 종료와 접수 실패가 모이는 반환 위치입니다.

### 3906행

```c

	*ret = rc;

```

마지막 전송 접수 상태를 호출자가 준 변수에 기록합니다.

### 3907행

```c

	return skb;

```

아직 전송하지 못한 첫 skb를 반환합니다. 모두 처리했으면 NULL입니다.

