← HardwareDUJINLABS.COM

Hardware 04 · Ethernet · Linux v6.18.37

Ethernet DMA Descriptor에서 NAPI와 Socket까지

wire의 Ethernet frame이 MAC filter, RX descriptor, DMA ownership, interrupt moderation, NAPI budget, skb/GRO, IP와 socket receive queue로 이동하는 전 과정을 검증합니다.

검증 깊이
board → Linux 8 layers
그림
4 boundary diagrams
원본 코드
5 excerpts · 335 lines
기준
Linux v6.18.37

01 · QUESTION

무엇을 증명할 것인가

PHY link와 MAC RX counter는 정상인데 application이 packet을 받지 못할 때 frame은 정확히 어느 queue와 context에서 멈췄는가?

wire의 Ethernet frame이 MAC filter, RX descriptor, DMA ownership, interrupt moderation, NAPI budget, skb/GRO, IP와 socket receive queue로 이동하는 전 과정을 검증합니다. 이 글은 Linux 로그를 출발점으로 삼지 않습니다. 같은 사건이 보드, controller, firmware, kernel에서 어떤 이름과 번호로 바뀌는지 아래에서 위로 연결합니다.

02 · CONTRACT

입력과 완료 조건

구분확인할 내용
입력정상 PHY/MAC link, DMA 가능한 memory, RX/TX ring, interrupt vector와 열린 net_device
출력RX packet이 socket queue까지 전달되고 TX completion이 descriptor와 BQL에 반영되는 상태
소유권descriptor ownership bit가 CPU/device 소유를 나누고 skb reference가 Linux stack의 packet lifetime을 소유
완료 조건wire/MAC/ring/NAPI/netstack/socket의 packet count가 동일한 test flow에서 설명 가능

03 · BOTTOM-UP

완전히 아래에서 시작하는 8단계

그림 1. 아래에서 Linux까지 올라오는 8개 검증 경계위 계층의 로그보다 아래 계층의 물리 증거를 먼저 확정합니다.
01
0. Frame · Preamble, MAC header, payload와 FCS

MAC은 preamble/SFD로 수신을 동기화하고 destination/source MAC, EtherType/length, payload와 FCS를 처리한다. VLAN tag, pause frame, jumbo MTU와 minimum frame padding이 descriptor length와 offload metadata에 영향을 준다.

증거: external capture, MAC good/bad frame counter, frame length histogram

START
02
1. Filter · MAC address, multicast hash와 VLAN filter

promiscuous, unicast perfect filter, multicast hash와 VLAN filter가 frame을 DMA로 보낼지 결정한다. hardware drop counter와 Linux `rx_dropped`는 같은 위치의 counter가 아니다. filter 설정 직후 readback이 필요하다.

증거: MAC filter register, multicast list, VLAN table, missed/filtered frame counter

CHECKPOINT
03
2. Ring · Descriptor base, length, producer/consumer와 ownership

driver는 coherent descriptor ring과 mapped data buffer를 준비한다. CPU가 buffer address와 length를 쓰고 ownership을 device로 넘긴다. device는 frame을 DMA한 뒤 status/length를 쓰고 ownership을 CPU로 돌린다. wrap bit와 tail pointer 단위가 틀리면 한 바퀴 뒤에 멈춘다.

증거: ring base/size, descriptor raw dump, head/tail, ownership bit와 DMA address

CHECKPOINT
04
3. IRQ · completion interrupt와 moderation

packet마다 IRQ를 내면 고속 traffic에서 CPU가 interrupt에 잠긴다. controller는 packet count/timer 기반 coalescing으로 interrupt를 줄인다. handler는 status를 ack하고 RX IRQ를 mask한 뒤 NAPI를 schedule하는 최소 작업만 수행한다.

증거: IRQ status/mask, coalescing parameter, `/proc/interrupts`, interrupt-to-packet ratio

CHECKPOINT
05
4. NAPI · poll budget과 interrupt 재활성화

NET_RX_SOFTIRQ가 NAPI poll callback을 호출해 budget만큼 descriptor를 회수한다. work가 budget보다 작고 ring이 비었을 때 `napi_complete_done()` 뒤 IRQ를 다시 연다. complete와 새 packet race를 driver가 올바르게 처리해야 lost interrupt를 막는다.

증거: napi poll/complete trace, budget/work, ring pending와 IRQ mask timestamp

CHECKPOINT
06
5. skb/GRO · descriptor metadata를 sk_buff로 변환

driver가 length, checksum, VLAN, RSS hash와 timestamp를 skb metadata로 옮긴다. page-pool과 build_skb 경로에서는 DMA buffer page lifetime이 skb와 연결된다. GRO는 같은 flow packet을 합쳐 protocol processing 비용을 줄인다.

증거: skb tracepoint, checksum/VLAN/RSS flag, page_pool stats, GRO count

CHECKPOINT
07
6. Stack · EtherType → IP → transport → socket

`__netif_receive_skb_core()`가 packet_type과 rx_handler를 통해 protocol을 선택한다. IPv4/IPv6 routing, netfilter와 TCP/UDP lookup을 거쳐 socket receive queue에 skb가 들어간다. 이 구간의 drop은 NIC statistics보다 protocol SNMP와 tracepoint에서 보인다.

증거: netif_receive trace, drop reason, IP/TCP/UDP counter, socket queue length

CHECKPOINT
08
7. Userspace · poll/epoll readiness와 read

socket queue가 비어 있지 않으면 wait queue를 깨우고 epoll readiness가 생성된다. application buffer보다 datagram이 크거나 TCP framing을 잘못하면 수신은 성공해도 application protocol이 깨질 수 있다. hardware 검증은 packet sequence와 timestamp를 application까지 유지해야 끝난다.

증거: strace/ss, socket drop, packet sequence, application timestamp

LINUX / RESULT
01

0. Frame · Preamble, MAC header, payload와 FCS

MAC은 preamble/SFD로 수신을 동기화하고 destination/source MAC, EtherType/length, payload와 FCS를 처리한다. VLAN tag, pause frame, jumbo MTU와 minimum frame padding이 descriptor length와 offload metadata에 영향을 준다.

남길 증거: external capture, MAC good/bad frame counter, frame length histogram

PHY symbol은 정상이어도 FCS/align error면 DMA 전에 MAC에서 drop될 수 있다.

02

1. Filter · MAC address, multicast hash와 VLAN filter

promiscuous, unicast perfect filter, multicast hash와 VLAN filter가 frame을 DMA로 보낼지 결정한다. hardware drop counter와 Linux `rx_dropped`는 같은 위치의 counter가 아니다. filter 설정 직후 readback이 필요하다.

남길 증거: MAC filter register, multicast list, VLAN table, missed/filtered frame counter

ring을 보기 전에 frame이 MAC filter를 통과했는지 확인한다.

03

2. Ring · Descriptor base, length, producer/consumer와 ownership

driver는 coherent descriptor ring과 mapped data buffer를 준비한다. CPU가 buffer address와 length를 쓰고 ownership을 device로 넘긴다. device는 frame을 DMA한 뒤 status/length를 쓰고 ownership을 CPU로 돌린다. wrap bit와 tail pointer 단위가 틀리면 한 바퀴 뒤에 멈춘다.

남길 증거: ring base/size, descriptor raw dump, head/tail, ownership bit와 DMA address

descriptor와 buffer의 DMA address를 CPU virtual address와 혼동하지 않는다.

04

3. IRQ · completion interrupt와 moderation

packet마다 IRQ를 내면 고속 traffic에서 CPU가 interrupt에 잠긴다. controller는 packet count/timer 기반 coalescing으로 interrupt를 줄인다. handler는 status를 ack하고 RX IRQ를 mask한 뒤 NAPI를 schedule하는 최소 작업만 수행한다.

남길 증거: IRQ status/mask, coalescing parameter, `/proc/interrupts`, interrupt-to-packet ratio

IRQ가 없다고 DMA가 없는 것은 아니며 polling/interrupt mask 상태를 함께 본다.

05

4. NAPI · poll budget과 interrupt 재활성화

NET_RX_SOFTIRQ가 NAPI poll callback을 호출해 budget만큼 descriptor를 회수한다. work가 budget보다 작고 ring이 비었을 때 `napi_complete_done()` 뒤 IRQ를 다시 연다. complete와 새 packet race를 driver가 올바르게 처리해야 lost interrupt를 막는다.

남길 증거: napi poll/complete trace, budget/work, ring pending와 IRQ mask timestamp

budget을 모두 썼다면 NAPI는 계속 scheduled 상태여야 하며 IRQ를 섣불리 열지 않는다.

06

5. skb/GRO · descriptor metadata를 sk_buff로 변환

driver가 length, checksum, VLAN, RSS hash와 timestamp를 skb metadata로 옮긴다. page-pool과 build_skb 경로에서는 DMA buffer page lifetime이 skb와 연결된다. GRO는 같은 flow packet을 합쳐 protocol processing 비용을 줄인다.

남길 증거: skb tracepoint, checksum/VLAN/RSS flag, page_pool stats, GRO count

checksum status를 잘못 번역하면 정상 packet을 stack이 drop하거나 손상 packet을 신뢰한다.

07

6. Stack · EtherType → IP → transport → socket

`__netif_receive_skb_core()`가 packet_type과 rx_handler를 통해 protocol을 선택한다. IPv4/IPv6 routing, netfilter와 TCP/UDP lookup을 거쳐 socket receive queue에 skb가 들어간다. 이 구간의 drop은 NIC statistics보다 protocol SNMP와 tracepoint에서 보인다.

남길 증거: netif_receive trace, drop reason, IP/TCP/UDP counter, socket queue length

NIC `rx_packets`와 application read count 사이의 모든 software drop point를 구분한다.

08

7. Userspace · poll/epoll readiness와 read

socket queue가 비어 있지 않으면 wait queue를 깨우고 epoll readiness가 생성된다. application buffer보다 datagram이 크거나 TCP framing을 잘못하면 수신은 성공해도 application protocol이 깨질 수 있다. hardware 검증은 packet sequence와 timestamp를 application까지 유지해야 끝난다.

남길 증거: strace/ss, socket drop, packet sequence, application timestamp

ping 성공만으로 RX/TX ring wrap, jumbo, checksum, multicast와 sustained load를 검증했다고 보지 않는다.

04 · DIAGRAMS

주소와 상태가 이동하는 모습

그림 2. Ethernet 실행 경로왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 Wire frame
02 MAC filter/FCS
03 DMA descriptor
04 IRQ mask+NAPI
05 skb/GRO
06 IP/TCP/UDP
07 socket
그림 3. firmware와 Linux 번호 공간의 연결왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 skb/qdisc
02 ndo_start_xmit
03 TX descriptor
04 dma_wmb+doorbell
05 MAC wire
06 TX completion
07 BQL wake
그림 4. 입력, 소유권, 완료 조건의 경계완료 조건을 충족하기 전에는 다음 계층이 해당 자원을 재사용하면 안 됩니다.
INPUT

정상 PHY/MAC link, DMA 가능한 memory, RX/TX ring, interrupt vector와 열린 net_device

OWNER

descriptor ownership bit가 CPU/device 소유를 나누고 skb reference가 Linux stack의 packet lifetime을 소유

OUTPUT

RX packet이 socket queue까지 전달되고 TX completion이 descriptor와 BQL에 반영되는 상태

DONE

wire/MAC/ring/NAPI/netstack/socket의 packet count가 동일한 test flow에서 설명 가능

05 · DETAILED MANUAL

DWMAC descriptor에서 NAPI와 socket까지 이어 읽는 상세 매뉴얼

Synopsys GMAC/GMAC4/XGMAC 계열을 기준으로 HW feature register, DMA channel, descriptor ownership, tail pointer, interrupt status, NAPI budget, skb와 page_pool lifetime을 이어서 봅니다. IP release와 합성 옵션에 따라 descriptor와 register가 달라지므로 먼저 capability를 읽고 해당 경로만 해석합니다.

공개적으로 다시 확인할 수 있는 자료

공개되지 않은 vendor register나 integration별 offset을 추측해서 채우지 않았습니다. 아래 제조사 자료, architecture 문서와 Linux v6.18.37 원본에서 다시 확인할 수 있는 범위만 사용했습니다.

Linux stmmac driver

Synopsys GMAC/GMAC4/XGMAC, descriptor, NAPI, PTP와 offload 공개 설명

Linux NAPI

schedule, poll budget, completion과 IRQ 재활성화 계약

06 · REGISTERS

읽어야 할 상태와 해석

값 하나만 떼어 보지 말고 동일 사건의 enable, status, ownership, completion register를 한 줄에 기록합니다. read-to-clear나 write-one-to-clear 속성은 반드시 datasheet에서 먼저 확인합니다.

레지스터/상태소유 블록검증할 의미
MAC RX/TX controlMAC blockenable, duplex, speed, checksum/filter
DMA descriptor baseDMA blockphysical/IOVA base와 alignment
Ring length/tailDMA blockdescriptor count 단위와 wrap
DMA status/maskDMA blockRX/TX completion, error, stopped state
Coalescing timer/countDMA blocklatency와 IRQ rate tradeoff
MTL/FIFO statusMAC/DMA 사이underflow, overflow와 queue backpressure

07 · LINUX SOURCE

Linux v6.18.37 원본 코드와 줄별 해설

라인 번호를 고정해 외우는 대신 함수 선언을 기준으로 Linux v6.18.37 tree에서 다시 찾았습니다. 로컬 tree에 있는 파일은 실제 코드를 직접 싣고, 나머지는 원본 파일과 읽을 함수 좌표를 연결했습니다.

Linux v6.18.37 · 실제 원본 코드

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로

drivers/net/ethernet/stmicro/stmmac/stmmac_main.c:4452-4542

4452/**
4453 *  stmmac_xmit - Tx entry point of the driver
4454 *  @skb : the socket buffer
4455 *  @dev : device pointer
4456 *  Description : this is the tx entry point of the driver.
4457 *  It programs the chain or the ring and supports oversized frames
4458 *  and SG feature.
4459 */
4460static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
4461{
4462	unsigned int first_entry, tx_packets, enh_desc;
4463	struct stmmac_priv *priv = netdev_priv(dev);
4464	unsigned int nopaged_len = skb_headlen(skb);
4465	int i, csum_insertion = 0, is_jumbo = 0;
4466	u32 queue = skb_get_queue_mapping(skb);
4467	int nfrags = skb_shinfo(skb)->nr_frags;
4468	int gso = skb_shinfo(skb)->gso_type;
4469	struct stmmac_txq_stats *txq_stats;
4470	struct dma_edesc *tbs_desc = NULL;
4471	struct dma_desc *desc, *first;
4472	struct stmmac_tx_queue *tx_q;
4473	bool has_vlan, set_ic;
4474	int entry, first_tx;
4475	dma_addr_t des;
4476	u32 sdu_len;
4477 
4478	tx_q = &priv->dma_conf.tx_queue[queue];
4479	txq_stats = &priv->xstats.txq_stats[queue];
4480	first_tx = tx_q->cur_tx;
4481 
4482	if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en)
4483		stmmac_stop_sw_lpi(priv);
4484 
4485	/* Manage oversized TCP frames for GMAC4 device */
4486	if (skb_is_gso(skb) && priv->tso) {
4487		if (gso & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
4488			return stmmac_tso_xmit(skb, dev);
4489		if (priv->plat->core_type == DWMAC_CORE_GMAC4 &&
4490		    (gso & SKB_GSO_UDP_L4))
4491			return stmmac_tso_xmit(skb, dev);
4492	}
4493 
4494	if (priv->est && priv->est->enable &&
4495	    priv->est->max_sdu[queue]) {
4496		sdu_len = skb->len;
4497		/* Add VLAN tag length if VLAN tag insertion offload is requested */
4498		if (priv->dma_cap.vlins && skb_vlan_tag_present(skb))
4499			sdu_len += VLAN_HLEN;
4500		if (sdu_len > priv->est->max_sdu[queue]) {
4501			priv->xstats.max_sdu_txq_drop[queue]++;
4502			goto max_sdu_err;
4503		}
4504	}
4505 
4506	if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) {
4507		if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
4508			netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
4509								queue));
4510			/* This is a hard error, log it. */
4511			netdev_err(priv->dev,
4512				   "%s: Tx Ring full when queue awake\n",
4513				   __func__);
4514		}
4515		return NETDEV_TX_BUSY;
4516	}
4517 
4518	/* Check if VLAN can be inserted by HW */
4519	has_vlan = stmmac_vlan_insert(priv, skb, tx_q);
4520 
4521	entry = tx_q->cur_tx;
4522	first_entry = entry;
4523	WARN_ON(tx_q->tx_skbuff[first_entry]);
4524 
4525	csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
4526	/* DWMAC IPs can be synthesized to support tx coe only for a few tx
4527	 * queues. In that case, checksum offloading for those queues that don't
4528	 * support tx coe needs to fallback to software checksum calculation.
4529	 *
4530	 * Packets that won't trigger the COE e.g. most DSA-tagged packets will
4531	 * also have to be checksummed in software.
4532	 */
4533	if (csum_insertion &&
4534	    (priv->plat->tx_queues_cfg[queue].coe_unsupported ||
4535	     !stmmac_has_ip_ethertype(skb))) {
4536		if (unlikely(skb_checksum_help(skb)))
4537			goto dma_map_err;
4538		csum_insertion = !csum_insertion;
4539	}
4540 
4541	if (likely(priv->extend_desc))
4542		desc = (struct dma_desc *)(tx_q->dma_etx + entry);
L4452 /**

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4453 * stmmac_xmit - Tx entry point of the driver

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4454 * @skb : the socket buffer

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4455 * @dev : device pointer

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4456 * Description : this is the tx entry point of the driver.

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4457 * It programs the chain or the ring and supports oversized frames

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4458 * and SG feature.

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4459 */

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4460 static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4461 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L4462 unsigned int first_entry, tx_packets, enh_desc;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4463 struct stmmac_priv *priv = netdev_priv(dev);

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4464 unsigned int nopaged_len = skb_headlen(skb);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 입력 계약을 만족하는지 확인합니다.

L4465 int i, csum_insertion = 0, is_jumbo = 0;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4466 u32 queue = skb_get_queue_mapping(skb);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 입력 계약을 만족하는지 확인합니다.

L4467 int nfrags = skb_shinfo(skb)->nr_frags;

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 입력 계약을 만족하는지 확인합니다.

L4468 int gso = skb_shinfo(skb)->gso_type;

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 입력 계약을 만족하는지 확인합니다.

L4469 struct stmmac_txq_stats *txq_stats;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4470 struct dma_edesc *tbs_desc = NULL;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4471 struct dma_desc *desc, *first;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4472 struct stmmac_tx_queue *tx_q;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4473 bool has_vlan, set_ic;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4474 int entry, first_tx;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4475 dma_addr_t des;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4476 u32 sdu_len;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4477 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4478 tx_q = &priv->dma_conf.tx_queue[queue];

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4479 txq_stats = &priv->xstats.txq_stats[queue];

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4480 first_tx = tx_q->cur_tx;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4481 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4482 if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4483 stmmac_stop_sw_lpi(priv);

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4484 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4485 /* Manage oversized TCP frames for GMAC4 device */

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4486 if (skb_is_gso(skb) && priv->tso) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4487 if (gso & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4488 return stmmac_tso_xmit(skb, dev);

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L4489 if (priv->plat->core_type == DWMAC_CORE_GMAC4 &&

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4490 (gso & SKB_GSO_UDP_L4))

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4491 return stmmac_tso_xmit(skb, dev);

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L4492 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L4493 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4494 if (priv->est && priv->est->enable &&

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4495 priv->est->max_sdu[queue]) {

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4496 sdu_len = skb->len;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4497 /* Add VLAN tag length if VLAN tag insertion offload is requested */

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4498 if (priv->dma_cap.vlins && skb_vlan_tag_present(skb))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4499 sdu_len += VLAN_HLEN;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4500 if (sdu_len > priv->est->max_sdu[queue]) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4501 priv->xstats.max_sdu_txq_drop[queue]++;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4502 goto max_sdu_err;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L4503 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L4504 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L4505 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4506 if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4507 if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4508 netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4509 queue));

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4510 /* This is a hard error, log it. */

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4511 netdev_err(priv->dev,

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4512 "%s: Tx Ring full when queue awake\n",

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4513 __func__);

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4514 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L4515 return NETDEV_TX_BUSY;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L4516 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L4517 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4518 /* Check if VLAN can be inserted by HW */

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4519 has_vlan = stmmac_vlan_insert(priv, skb, tx_q);

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4520 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4521 entry = tx_q->cur_tx;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4522 first_entry = entry;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4523 WARN_ON(tx_q->tx_skbuff[first_entry]);

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4524 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4525 csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4526 /* DWMAC IPs can be synthesized to support tx coe only for a few tx

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4527 * queues. In that case, checksum offloading for those queues that don't

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4528 * support tx coe needs to fallback to software checksum calculation.

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4529 *

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4530 * Packets that won't trigger the COE e.g. most DSA-tagged packets will

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4531 * also have to be checksummed in software.

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4532 */

원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L4533 if (csum_insertion &&

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4534 (priv->plat->tx_queues_cfg[queue].coe_unsupported ||

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4535 !stmmac_has_ip_ethertype(skb))) {

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4536 if (unlikely(skb_checksum_help(skb)))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4537 goto dma_map_err;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L4538 csum_insertion = !csum_insertion;

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L4539 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L4540 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L4541 if (likely(priv->extend_desc))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L4542 desc = (struct dma_desc *)(tx_q->dma_etx + entry);

skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

Linux v6.18.37 · 실제 원본 코드

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll

drivers/net/ethernet/stmicro/stmmac/stmmac_main.c:5685-5737

5685	u64_stats_update_end(&rxq_stats->napi_syncp);
5686 
5687	priv->xstats.rx_dropped += rx_dropped;
5688	priv->xstats.rx_errors += rx_errors;
5689 
5690	return count;
5691}
5692 
5693static int stmmac_napi_poll_rx(struct napi_struct *napi, int budget)
5694{
5695	struct stmmac_channel *ch =
5696		container_of(napi, struct stmmac_channel, rx_napi);
5697	struct stmmac_priv *priv = ch->priv_data;
5698	struct stmmac_rxq_stats *rxq_stats;
5699	u32 chan = ch->index;
5700	int work_done;
5701 
5702	rxq_stats = &priv->xstats.rxq_stats[chan];
5703	u64_stats_update_begin(&rxq_stats->napi_syncp);
5704	u64_stats_inc(&rxq_stats->napi.poll);
5705	u64_stats_update_end(&rxq_stats->napi_syncp);
5706 
5707	work_done = stmmac_rx(priv, budget, chan);
5708	if (work_done < budget && napi_complete_done(napi, work_done)) {
5709		unsigned long flags;
5710 
5711		spin_lock_irqsave(&ch->lock, flags);
5712		stmmac_enable_dma_irq(priv, priv->ioaddr, chan, 1, 0);
5713		spin_unlock_irqrestore(&ch->lock, flags);
5714	}
5715 
5716	return work_done;
5717}
5718 
5719static int stmmac_napi_poll_tx(struct napi_struct *napi, int budget)
5720{
5721	struct stmmac_channel *ch =
5722		container_of(napi, struct stmmac_channel, tx_napi);
5723	struct stmmac_priv *priv = ch->priv_data;
5724	struct stmmac_txq_stats *txq_stats;
5725	bool pending_packets = false;
5726	u32 chan = ch->index;
5727	int work_done;
5728 
5729	txq_stats = &priv->xstats.txq_stats[chan];
5730	u64_stats_update_begin(&txq_stats->napi_syncp);
5731	u64_stats_inc(&txq_stats->napi.poll);
5732	u64_stats_update_end(&txq_stats->napi_syncp);
5733 
5734	work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets);
5735	work_done = min(work_done, budget);
5736 
5737	if (work_done < budget && napi_complete_done(napi, work_done)) {
L5685 u64_stats_update_end(&rxq_stats->napi_syncp);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5686 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5687 priv->xstats.rx_dropped += rx_dropped;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5688 priv->xstats.rx_errors += rx_errors;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5689 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5690 return count;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L5691 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5692 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5693 static int stmmac_napi_poll_rx(struct napi_struct *napi, int budget)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 입력 계약을 만족하는지 확인합니다.

L5694 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5695 struct stmmac_channel *ch =

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5696 container_of(napi, struct stmmac_channel, rx_napi);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5697 struct stmmac_priv *priv = ch->priv_data;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5698 struct stmmac_rxq_stats *rxq_stats;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5699 u32 chan = ch->index;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5700 int work_done;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5701 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5702 rxq_stats = &priv->xstats.rxq_stats[chan];

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5703 u64_stats_update_begin(&rxq_stats->napi_syncp);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5704 u64_stats_inc(&rxq_stats->napi.poll);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5705 u64_stats_update_end(&rxq_stats->napi_syncp);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5706 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5707 work_done = stmmac_rx(priv, budget, chan);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5708 if (work_done < budget && napi_complete_done(napi, work_done)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L5709 unsigned long flags;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5710 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5711 spin_lock_irqsave(&ch->lock, flags);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5712 stmmac_enable_dma_irq(priv, priv->ioaddr, chan, 1, 0);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5713 spin_unlock_irqrestore(&ch->lock, flags);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5714 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5715 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5716 return work_done;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L5717 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5718 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5719 static int stmmac_napi_poll_tx(struct napi_struct *napi, int budget)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 입력 계약을 만족하는지 확인합니다.

L5720 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5721 struct stmmac_channel *ch =

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5722 container_of(napi, struct stmmac_channel, tx_napi);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5723 struct stmmac_priv *priv = ch->priv_data;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5724 struct stmmac_txq_stats *txq_stats;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5725 bool pending_packets = false;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5726 u32 chan = ch->index;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5727 int work_done;

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5728 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5729 txq_stats = &priv->xstats.txq_stats[chan];

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5730 u64_stats_update_begin(&txq_stats->napi_syncp);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5731 u64_stats_inc(&txq_stats->napi.poll);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5732 u64_stats_update_end(&txq_stats->napi_syncp);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5733 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5734 work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5735 work_done = min(work_done, budget);

RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5736 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5737 if (work_done < budget && napi_complete_done(napi, work_done)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

Linux v6.18.37 · 실제 원본 코드

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR

drivers/net/ethernet/stmicro/stmmac/stmmac_main.c:121-173

121 
122/* By default the driver will use the ring mode to manage tx and rx descriptors,
123 * but allow user to force to use the chain instead of the ring
124 */
125static unsigned int chain_mode;
126module_param(chain_mode, int, 0444);
127MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");
128 
129static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
130/* For MSI interrupts handling */
131static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id);
132static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id);
133static irqreturn_t stmmac_msi_intr_tx(int irq, void *data);
134static irqreturn_t stmmac_msi_intr_rx(int irq, void *data);
135static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue);
136static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue);
137static void stmmac_reset_queues_param(struct stmmac_priv *priv);
138static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);
139static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
140static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
141					  u32 rxmode, u32 chan);
142static void stmmac_vlan_restore(struct stmmac_priv *priv);
143 
144#ifdef CONFIG_DEBUG_FS
145static const struct net_device_ops stmmac_netdev_ops;
146static void stmmac_init_fs(struct net_device *dev);
147static void stmmac_exit_fs(struct net_device *dev);
148#endif
149 
150#define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC))
151 
152/**
153 * stmmac_set_clk_tx_rate() - set the clock rate for the MAC transmit clock
154 * @bsp_priv: BSP private data structure (unused)
155 * @clk_tx_i: the transmit clock
156 * @interface: the selected interface mode
157 * @speed: the speed that the MAC will be operating at
158 *
159 * Set the transmit clock rate for the MAC, normally 2.5MHz for 10Mbps,
160 * 25MHz for 100Mbps and 125MHz for 1Gbps. This is suitable for at least
161 * MII, GMII, RGMII and RMII interface modes. Platforms can hook this into
162 * the plat_data->set_clk_tx_rate method directly, call it via their own
163 * implementation, or implement their own method should they have more
164 * complex requirements. It is intended to only be used in this method.
165 *
166 * plat_data->clk_tx_i must be filled in.
167 */
168int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
169			   phy_interface_t interface, int speed)
170{
171	long rate = rgmii_clock(speed);
172 
173	/* Silently ignore unsupported speeds as rgmii_clock() only
L121 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L122 /* By default the driver will use the ring mode to manage tx and rx descriptors,

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L123 * but allow user to force to use the chain instead of the ring

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L124 */

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L125 static unsigned int chain_mode;

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L126 module_param(chain_mode, int, 0444);

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L127 MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L128 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L129 static irqreturn_t stmmac_interrupt(int irq, void *dev_id);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L130 /* For MSI interrupts handling */

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L131 static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L132 static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L133 static irqreturn_t stmmac_msi_intr_tx(int irq, void *data);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L134 static irqreturn_t stmmac_msi_intr_rx(int irq, void *data);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L135 static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L136 static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L137 static void stmmac_reset_queues_param(struct stmmac_priv *priv);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L138 static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L139 static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L140 static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L141 u32 rxmode, u32 chan);

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L142 static void stmmac_vlan_restore(struct stmmac_priv *priv);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L143 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L144 #ifdef CONFIG_DEBUG_FS

빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.

L145 static const struct net_device_ops stmmac_netdev_ops;

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L146 static void stmmac_init_fs(struct net_device *dev);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L147 static void stmmac_exit_fs(struct net_device *dev);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L148 #endif

빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.

L149 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L150 #define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC))

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L151 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L152 /**

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L153 * stmmac_set_clk_tx_rate() - set the clock rate for the MAC transmit clock

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L154 * @bsp_priv: BSP private data structure (unused)

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L155 * @clk_tx_i: the transmit clock

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L156 * @interface: the selected interface mode

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L157 * @speed: the speed that the MAC will be operating at

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L158 *

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L159 * Set the transmit clock rate for the MAC, normally 2.5MHz for 10Mbps,

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L160 * 25MHz for 100Mbps and 125MHz for 1Gbps. This is suitable for at least

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L161 * MII, GMII, RGMII and RMII interface modes. Platforms can hook this into

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L162 * the plat_data->set_clk_tx_rate method directly, call it via their own

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L163 * implementation, or implement their own method should they have more

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L164 * complex requirements. It is intended to only be used in this method.

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L165 *

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L166 * plat_data->clk_tx_i must be filled in.

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L167 */

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L168 int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.

L169 phy_interface_t interface, int speed)

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L170 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L171 long rate = rgmii_clock(speed);

MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L172 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L173 /* Silently ignore unsupported speeds as rgmii_clock() only

원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

Linux v6.18.37 · 실제 원본 코드

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core

net/core/dev.c:7779-7843

7779	struct napi_struct *napi = data;
7780 
7781	while (!napi_thread_wait(napi))
7782		napi_threaded_poll_loop(napi);
7783 
7784	return 0;
7785}
7786 
7787static __latent_entropy void net_rx_action(void)
7788{
7789	struct softnet_data *sd = this_cpu_ptr(&softnet_data);
7790	unsigned long time_limit = jiffies +
7791		usecs_to_jiffies(READ_ONCE(net_hotdata.netdev_budget_usecs));
7792	struct bpf_net_context __bpf_net_ctx, *bpf_net_ctx;
7793	int budget = READ_ONCE(net_hotdata.netdev_budget);
7794	LIST_HEAD(list);
7795	LIST_HEAD(repoll);
7796 
7797	bpf_net_ctx = bpf_net_ctx_set(&__bpf_net_ctx);
7798start:
7799	sd->in_net_rx_action = true;
7800	local_irq_disable();
7801	list_splice_init(&sd->poll_list, &list);
7802	local_irq_enable();
7803 
7804	for (;;) {
7805		struct napi_struct *n;
7806 
7807		skb_defer_free_flush();
7808 
7809		if (list_empty(&list)) {
7810			if (list_empty(&repoll)) {
7811				sd->in_net_rx_action = false;
7812				barrier();
7813				/* We need to check if ____napi_schedule()
7814				 * had refilled poll_list while
7815				 * sd->in_net_rx_action was true.
7816				 */
7817				if (!list_empty(&sd->poll_list))
7818					goto start;
7819				if (!sd_has_rps_ipi_waiting(sd))
7820					goto end;
7821			}
7822			break;
7823		}
7824 
7825		n = list_first_entry(&list, struct napi_struct, poll_list);
7826		budget -= napi_poll(n, &repoll);
7827 
7828		/* If softirq window is exhausted then punt.
7829		 * Allow this to run for 2 jiffies since which will allow
7830		 * an average latency of 1.5/HZ.
7831		 */
7832		if (unlikely(budget <= 0 ||
7833			     time_after_eq(jiffies, time_limit))) {
7834			/* Pairs with READ_ONCE() in softnet_seq_show() */
7835			WRITE_ONCE(sd->time_squeeze, sd->time_squeeze + 1);
7836			break;
7837		}
7838	}
7839 
7840	local_irq_disable();
7841 
7842	list_splice_tail_init(&sd->poll_list, &list);
7843	list_splice_tail(&repoll, &list);
L7779 struct napi_struct *napi = data;

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7780 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7781 while (!napi_thread_wait(napi))

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L7782 napi_threaded_poll_loop(napi);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L7783 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7784 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L7785 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L7786 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7787 static __latent_entropy void net_rx_action(void)

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7788 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L7789 struct softnet_data *sd = this_cpu_ptr(&softnet_data);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7790 unsigned long time_limit = jiffies +

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7791 usecs_to_jiffies(READ_ONCE(net_hotdata.netdev_budget_usecs));

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L7792 struct bpf_net_context __bpf_net_ctx, *bpf_net_ctx;

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7793 int budget = READ_ONCE(net_hotdata.netdev_budget);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 입력 계약을 만족하는지 확인합니다.

L7794 LIST_HEAD(list);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7795 LIST_HEAD(repoll);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7796 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7797 bpf_net_ctx = bpf_net_ctx_set(&__bpf_net_ctx);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7798 start:

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7799 sd->in_net_rx_action = true;

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7800 local_irq_disable();

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7801 list_splice_init(&sd->poll_list, &list);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7802 local_irq_enable();

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7803 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7804 for (;;) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L7805 struct napi_struct *n;

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7806 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7807 skb_defer_free_flush();

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7808 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7809 if (list_empty(&list)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L7810 if (list_empty(&repoll)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L7811 sd->in_net_rx_action = false;

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7812 barrier();

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7813 /* We need to check if ____napi_schedule()

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7814 * had refilled poll_list while

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7815 * sd->in_net_rx_action was true.

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7816 */

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7817 if (!list_empty(&sd->poll_list))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L7818 goto start;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L7819 if (!sd_has_rps_ipi_waiting(sd))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L7820 goto end;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L7821 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L7822 break;

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7823 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L7824 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7825 n = list_first_entry(&list, struct napi_struct, poll_list);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7826 budget -= napi_poll(n, &repoll);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7827 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7828 /* If softirq window is exhausted then punt.

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7829 * Allow this to run for 2 jiffies since which will allow

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7830 * an average latency of 1.5/HZ.

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7831 */

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7832 if (unlikely(budget <= 0 ||

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L7833 time_after_eq(jiffies, time_limit))) {

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7834 /* Pairs with READ_ONCE() in softnet_seq_show() */

원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L7835 WRITE_ONCE(sd->time_squeeze, sd->time_squeeze + 1);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L7836 break;

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7837 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L7838 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L7839 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7840 local_irq_disable();

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7841 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L7842 list_splice_tail_init(&sd->poll_list, &list);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L7843 list_splice_tail(&repoll, &list);

CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

Linux v6.18.37 · 실제 원본 코드

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core

net/core/dev.c:5880-5952

5880		rcu_read_lock();
5881		ingress_retval = nf_hook_ingress(skb);
5882		rcu_read_unlock();
5883		return ingress_retval;
5884	}
5885	return 0;
5886}
5887 
5888static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
5889				    struct packet_type **ppt_prev)
5890{
5891	enum skb_drop_reason drop_reason = SKB_DROP_REASON_UNHANDLED_PROTO;
5892	struct packet_type *ptype, *pt_prev;
5893	rx_handler_func_t *rx_handler;
5894	struct sk_buff *skb = *pskb;
5895	struct net_device *orig_dev;
5896	bool deliver_exact = false;
5897	int ret = NET_RX_DROP;
5898	__be16 type;
5899 
5900	net_timestamp_check(!READ_ONCE(net_hotdata.tstamp_prequeue), skb);
5901 
5902	trace_netif_receive_skb(skb);
5903 
5904	orig_dev = skb->dev;
5905 
5906	skb_reset_network_header(skb);
5907#if !defined(CONFIG_DEBUG_NET)
5908	/* We plan to no longer reset the transport header here.
5909	 * Give some time to fuzzers and dev build to catch bugs
5910	 * in network stacks.
5911	 */
5912	if (!skb_transport_header_was_set(skb))
5913		skb_reset_transport_header(skb);
5914#endif
5915	skb_reset_mac_len(skb);
5916 
5917	pt_prev = NULL;
5918 
5919another_round:
5920	skb->skb_iif = skb->dev->ifindex;
5921 
5922	__this_cpu_inc(softnet_data.processed);
5923 
5924	if (static_branch_unlikely(&generic_xdp_needed_key)) {
5925		int ret2;
5926 
5927		migrate_disable();
5928		ret2 = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog),
5929				      &skb);
5930		migrate_enable();
5931 
5932		if (ret2 != XDP_PASS) {
5933			ret = NET_RX_DROP;
5934			goto out;
5935		}
5936	}
5937 
5938	if (eth_type_vlan(skb->protocol)) {
5939		skb = skb_vlan_untag(skb);
5940		if (unlikely(!skb))
5941			goto out;
5942	}
5943 
5944	if (skb_skip_tc_classify(skb))
5945		goto skip_classify;
5946 
5947	if (pfmemalloc)
5948		goto skip_taps;
5949 
5950	list_for_each_entry_rcu(ptype, &dev_net_rcu(skb->dev)->ptype_all,
5951				list) {
5952		if (pt_prev)
L5880 rcu_read_lock();

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L5881 ingress_retval = nf_hook_ingress(skb);

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5882 rcu_read_unlock();

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L5883 return ingress_retval;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L5884 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5885 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L5886 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5887 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5888 static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 입력 계약을 만족하는지 확인합니다.

L5889 struct packet_type **ppt_prev)

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5890 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5891 enum skb_drop_reason drop_reason = SKB_DROP_REASON_UNHANDLED_PROTO;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5892 struct packet_type *ptype, *pt_prev;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5893 rx_handler_func_t *rx_handler;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5894 struct sk_buff *skb = *pskb;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5895 struct net_device *orig_dev;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5896 bool deliver_exact = false;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5897 int ret = NET_RX_DROP;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5898 __be16 type;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5899 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5900 net_timestamp_check(!READ_ONCE(net_hotdata.tstamp_prequeue), skb);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L5901 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5902 trace_netif_receive_skb(skb);

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5903 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5904 orig_dev = skb->dev;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5905 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5906 skb_reset_network_header(skb);

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5907 #if !defined(CONFIG_DEBUG_NET)

빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.

L5908 /* We plan to no longer reset the transport header here.

원본 주석입니다. driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L5909 * Give some time to fuzzers and dev build to catch bugs

원본 주석입니다. driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L5910 * in network stacks.

원본 주석입니다. driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L5911 */

원본 주석입니다. driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L5912 if (!skb_transport_header_was_set(skb))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L5913 skb_reset_transport_header(skb);

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5914 #endif

빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.

L5915 skb_reset_mac_len(skb);

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5916 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5917 pt_prev = NULL;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5918 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5919 another_round:

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5920 skb->skb_iif = skb->dev->ifindex;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5921 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5922 __this_cpu_inc(softnet_data.processed);

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5923 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5924 if (static_branch_unlikely(&generic_xdp_needed_key)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L5925 int ret2;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5926 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5927 migrate_disable();

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5928 ret2 = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog),

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5929 &skb);

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5930 migrate_enable();

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5931 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5932 if (ret2 != XDP_PASS) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L5933 ret = NET_RX_DROP;

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5934 goto out;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L5935 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5936 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5937 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5938 if (eth_type_vlan(skb->protocol)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L5939 skb = skb_vlan_untag(skb);

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5940 if (unlikely(!skb))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L5941 goto out;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L5942 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L5943 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5944 if (skb_skip_tc_classify(skb))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L5945 goto skip_classify;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L5946 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5947 if (pfmemalloc)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L5948 goto skip_taps;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L5949 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L5950 list_for_each_entry_rcu(ptype, &dev_net_rcu(skb->dev)->ptype_all,

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5951 list) {

driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L5952 if (pt_prev)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

Linux v6.18.37 · 원본 좌표

include/linux/netdevice.h

driver와 networking core 사이의 object 및 callback 계약

읽을 함수
struct napi_struct
읽을 함수
struct net_device_ops

Linux v6.18.37 원본 파일 열기 →

08 · VERIFICATION

실제 장비에서 확인하는 순서

  1. Counter ladder

    실행 외부 송신 packet 수와 PHY/MAC/DMA/netdev/socket 수를 한 표에 기록한다.

    통과 기준 처음 감소하는 두 checkpoint 사이가 실제 drop 위치다.

  2. Ring

    실행 traffic 정지 상태에서 descriptor, head/tail과 DMA address를 dump한다.

    통과 기준 ownership과 producer/consumer가 모순 없이 한 방향으로 진행해야 한다.

  3. NAPI

    실행 irq와 napi tracepoint를 함께 수집한다.

    통과 기준 IRQ mask → poll → complete → unmask 순서가 보여야 한다.

  4. Offload

    실행 RX checksum/GRO/TSO를 하나씩 끄고 결과를 비교한다.

    통과 기준 문제가 사라지는 feature가 metadata 또는 segmentation 경계를 좁힌다.

  5. Load

    실행 64B, MTU, jumbo와 burst traffic으로 ring wrap을 반복한다.

    통과 기준 drop/error 증가가 queue depth와 coalescing 설정으로 설명돼야 한다.

09 · FAILURE MATRIX

처음 끊긴 경계로 원인을 좁히기

관측 결과우선 의심할 경계다음 증거
MAC counter 0filter, MAC enable, PHY-MAC interface 문제wire capture와 MAC status
MAC 증가, descriptor 불변DMA enable/base/tail/IOMMU 문제ring register와 IOMMU fault
descriptor 완료, NAPI 없음IRQ mask/routing 또는 schedule 문제status, `/proc/interrupts`, napi state
NAPI 증가, rx_packets 없음descriptor parse, length/checksum/drop 문제driver error와 skb trace
rx_packets 증가, socket 없음protocol/netfilter/routing/socket lookup 문제drop reason과 protocol counter

핵심은 마지막 오류 메시지가 아니라 처음 기대값과 달라진 checkpoint입니다. 그보다 아래의 통과 증거와 그 지점의 실패 증거를 한 묶음으로 남겨야 수정의 효과도 검증할 수 있습니다.