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단계
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
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
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
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
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
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
`__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
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
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될 수 있다.
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를 통과했는지 확인한다.
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와 혼동하지 않는다.
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 상태를 함께 본다.
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를 섣불리 열지 않는다.
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을 신뢰한다.
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를 구분한다.
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
주소와 상태가 이동하는 모습
정상 PHY/MAC link, DMA 가능한 memory, RX/TX ring, interrupt vector와 열린 net_device
descriptor ownership bit가 CPU/device 소유를 나누고 skb reference가 Linux stack의 packet lifetime을 소유
RX packet이 socket queue까지 전달되고 TX completion이 descriptor와 BQL에 반영되는 상태
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 재활성화 계약
Linux v6.18.37 stmmac_main.c
open, xmit, RX, ISR와 NAPI poll 원본
Linux v6.18.37 dwmac4_dma.c
DMA channel, ring length, tail pointer와 interrupt enable
Linux v6.18.37 net/core/dev.c
NAPI core, NET_RX_SOFTIRQ와 receive stack 진입
Linux DMA API HOWTO
descriptor와 packet buffer의 CPU/device ownership, mapping과 barrier
06 · REGISTERS
읽어야 할 상태와 해석
값 하나만 떼어 보지 말고 동일 사건의 enable, status, ownership, completion register를 한 줄에 기록합니다. read-to-clear나 write-one-to-clear 속성은 반드시 datasheet에서 먼저 확인합니다.
| 레지스터/상태 | 소유 블록 | 검증할 의미 |
|---|---|---|
| MAC RX/TX control | MAC block | enable, duplex, speed, checksum/filter |
| DMA descriptor base | DMA block | physical/IOVA base와 alignment |
| Ring length/tail | DMA block | descriptor count 단위와 wrap |
| DMA status/mask | DMA block | RX/TX completion, error, stopped state |
| Coalescing timer/count | DMA block | latency와 IRQ rate tradeoff |
| MTL/FIFO status | MAC/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);/**
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* stmmac_xmit - Tx entry point of the driver
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* @skb : the socket buffer
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* @dev : device pointer
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* Description : this is the tx entry point of the driver.
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* It programs the chain or the ring and supports oversized frames
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* and SG feature.
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*/
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
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와 연결해 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
unsigned int first_entry, tx_packets, enh_desc;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct stmmac_priv *priv = netdev_priv(dev);
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
unsigned int nopaged_len = skb_headlen(skb);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 입력 계약을 만족하는지 확인합니다.
int i, csum_insertion = 0, is_jumbo = 0;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u32 queue = skb_get_queue_mapping(skb);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 입력 계약을 만족하는지 확인합니다.
int nfrags = skb_shinfo(skb)->nr_frags;
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 입력 계약을 만족하는지 확인합니다.
int gso = skb_shinfo(skb)->gso_type;
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 입력 계약을 만족하는지 확인합니다.
struct stmmac_txq_stats *txq_stats;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct dma_edesc *tbs_desc = NULL;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct dma_desc *desc, *first;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct stmmac_tx_queue *tx_q;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
bool has_vlan, set_ic;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
int entry, first_tx;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
dma_addr_t des;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u32 sdu_len;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
tx_q = &priv->dma_conf.tx_queue[queue];
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
txq_stats = &priv->xstats.txq_stats[queue];
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
first_tx = tx_q->cur_tx;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en)
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
stmmac_stop_sw_lpi(priv);
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* Manage oversized TCP frames for GMAC4 device */
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
if (skb_is_gso(skb) && priv->tso) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
if (gso & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
return stmmac_tso_xmit(skb, dev);
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
if (priv->plat->core_type == DWMAC_CORE_GMAC4 &&
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
(gso & SKB_GSO_UDP_L4))
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
return stmmac_tso_xmit(skb, dev);
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (priv->est && priv->est->enable &&
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
priv->est->max_sdu[queue]) {
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
sdu_len = skb->len;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
/* Add VLAN tag length if VLAN tag insertion offload is requested */
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
if (priv->dma_cap.vlins && skb_vlan_tag_present(skb))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
sdu_len += VLAN_HLEN;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
if (sdu_len > priv->est->max_sdu[queue]) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
priv->xstats.max_sdu_txq_drop[queue]++;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
goto max_sdu_err;
공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
netif_tx_stop_queue(netdev_get_tx_queue(priv->dev,
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
queue));
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
/* This is a hard error, log it. */
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
netdev_err(priv->dev,
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
"%s: Tx Ring full when queue awake\n",
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
__func__);
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
return NETDEV_TX_BUSY;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* Check if VLAN can be inserted by HW */
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
has_vlan = stmmac_vlan_insert(priv, skb, tx_q);
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
entry = tx_q->cur_tx;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
first_entry = entry;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
WARN_ON(tx_q->tx_skbuff[first_entry]);
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
/* DWMAC IPs can be synthesized to support tx coe only for a few tx
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* queues. In that case, checksum offloading for those queues that don't
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* support tx coe needs to fallback to software checksum calculation.
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* Packets that won't trigger the COE e.g. most DSA-tagged packets will
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* also have to be checksummed in software.
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*/
원본 주석입니다. skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
if (csum_insertion &&
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
(priv->plat->tx_queues_cfg[queue].coe_unsupported ||
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
!stmmac_has_ip_ethertype(skb))) {
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
if (unlikely(skb_checksum_help(skb)))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
goto dma_map_err;
공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.
csum_insertion = !csum_insertion;
skb를 queue와 descriptor에 매핑하고 DWMAC TX DMA에 ownership을 넘기는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (likely(priv->extend_desc))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
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)) {u64_stats_update_end(&rxq_stats->napi_syncp);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
priv->xstats.rx_dropped += rx_dropped;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
priv->xstats.rx_errors += rx_errors;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
return count;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static int stmmac_napi_poll_rx(struct napi_struct *napi, int budget)
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 입력 계약을 만족하는지 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
struct stmmac_channel *ch =
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
container_of(napi, struct stmmac_channel, rx_napi);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct stmmac_priv *priv = ch->priv_data;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct stmmac_rxq_stats *rxq_stats;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u32 chan = ch->index;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
int work_done;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
rxq_stats = &priv->xstats.rxq_stats[chan];
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u64_stats_update_begin(&rxq_stats->napi_syncp);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u64_stats_inc(&rxq_stats->napi.poll);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u64_stats_update_end(&rxq_stats->napi_syncp);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
work_done = stmmac_rx(priv, budget, chan);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
if (work_done < budget && napi_complete_done(napi, work_done)) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
unsigned long flags;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
spin_lock_irqsave(&ch->lock, flags);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
stmmac_enable_dma_irq(priv, priv->ioaddr, chan, 1, 0);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
spin_unlock_irqrestore(&ch->lock, flags);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
return work_done;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static int stmmac_napi_poll_tx(struct napi_struct *napi, int budget)
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 입력 계약을 만족하는지 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
struct stmmac_channel *ch =
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
container_of(napi, struct stmmac_channel, tx_napi);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct stmmac_priv *priv = ch->priv_data;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct stmmac_txq_stats *txq_stats;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
bool pending_packets = false;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u32 chan = ch->index;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
int work_done;
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
txq_stats = &priv->xstats.txq_stats[chan];
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u64_stats_update_begin(&txq_stats->napi_syncp);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u64_stats_inc(&txq_stats->napi.poll);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u64_stats_update_end(&txq_stats->napi_syncp);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
work_done = min(work_done, budget);
RX channel을 budget만큼 처리하고 ring이 비면 NAPI 완료와 DMA IRQ 재활성화를 수행하는 poll의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
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(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* By default the driver will use the ring mode to manage tx and rx descriptors,
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* but allow user to force to use the chain instead of the ring
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*/
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
static unsigned int chain_mode;
MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
module_param(chain_mode, int, 0444);
MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");
MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
/* For MSI interrupts handling */
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
static irqreturn_t stmmac_mac_interrupt(int irq, void *dev_id);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static irqreturn_t stmmac_msi_intr_tx(int irq, void *data);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static irqreturn_t stmmac_msi_intr_rx(int irq, void *data);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static void stmmac_reset_queues_param(struct stmmac_priv *priv);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
u32 rxmode, u32 chan);
MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
static void stmmac_vlan_restore(struct stmmac_priv *priv);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
#ifdef CONFIG_DEBUG_FS
빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.
static const struct net_device_ops stmmac_netdev_ops;
MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
static void stmmac_init_fs(struct net_device *dev);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
static void stmmac_exit_fs(struct net_device *dev);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
#endif
빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
#define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC))
MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/**
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* stmmac_set_clk_tx_rate() - set the clock rate for the MAC transmit clock
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* @bsp_priv: BSP private data structure (unused)
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* @clk_tx_i: the transmit clock
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* @interface: the selected interface mode
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* @speed: the speed that the MAC will be operating at
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* Set the transmit clock rate for the MAC, normally 2.5MHz for 10Mbps,
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* 25MHz for 100Mbps and 125MHz for 1Gbps. This is suitable for at least
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* MII, GMII, RGMII and RMII interface modes. Platforms can hook this into
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* the plat_data->set_clk_tx_rate method directly, call it via their own
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* implementation, or implement their own method should they have more
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* complex requirements. It is intended to only be used in this method.
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* plat_data->clk_tx_i must be filled in.
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*/
원본 주석입니다. MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 입력 계약을 만족하는지 확인합니다.
phy_interface_t interface, int speed)
MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
long rate = rgmii_clock(speed);
MAC common status와 DMA channel interrupt를 분류하는 최상위 ISR의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* 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
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);struct napi_struct *napi = data;
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
while (!napi_thread_wait(napi))
여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.
napi_threaded_poll_loop(napi);
공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
return 0;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static __latent_entropy void net_rx_action(void)
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
unsigned long time_limit = jiffies +
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
usecs_to_jiffies(READ_ONCE(net_hotdata.netdev_budget_usecs));
공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.
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와 연결해 확인합니다.
int budget = READ_ONCE(net_hotdata.netdev_budget);
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 입력 계약을 만족하는지 확인합니다.
LIST_HEAD(list);
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
LIST_HEAD(repoll);
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
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와 연결해 확인합니다.
start:
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
sd->in_net_rx_action = true;
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
local_irq_disable();
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
list_splice_init(&sd->poll_list, &list);
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
local_irq_enable();
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
for (;;) {
여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.
struct napi_struct *n;
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
skb_defer_free_flush();
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (list_empty(&list)) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
if (list_empty(&repoll)) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
sd->in_net_rx_action = false;
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
barrier();
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
/* We need to check if ____napi_schedule()
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* had refilled poll_list while
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* sd->in_net_rx_action was true.
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*/
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
if (!list_empty(&sd->poll_list))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
goto start;
공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.
if (!sd_has_rps_ipi_waiting(sd))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
goto end;
공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
break;
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
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와 연결해 확인합니다.
budget -= napi_poll(n, &repoll);
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* If softirq window is exhausted then punt.
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* Allow this to run for 2 jiffies since which will allow
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* an average latency of 1.5/HZ.
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*/
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
if (unlikely(budget <= 0 ||
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
time_after_eq(jiffies, time_limit))) {
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
/* Pairs with READ_ONCE() in softnet_seq_show() */
원본 주석입니다. CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
WRITE_ONCE(sd->time_squeeze, sd->time_squeeze + 1);
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
break;
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
local_irq_disable();
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
list_splice_tail_init(&sd->poll_list, &list);
CPU별 NAPI poll list를 NET_RX_SOFTIRQ context에서 실행하는 networking core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
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
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)rcu_read_lock();
공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.
ingress_retval = nf_hook_ingress(skb);
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
rcu_read_unlock();
공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.
return ingress_retval;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
return 0;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 입력 계약을 만족하는지 확인합니다.
struct packet_type **ppt_prev)
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
enum skb_drop_reason drop_reason = SKB_DROP_REASON_UNHANDLED_PROTO;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct packet_type *ptype, *pt_prev;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
rx_handler_func_t *rx_handler;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct sk_buff *skb = *pskb;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct net_device *orig_dev;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
bool deliver_exact = false;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
int ret = NET_RX_DROP;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
__be16 type;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
net_timestamp_check(!READ_ONCE(net_hotdata.tstamp_prequeue), skb);
공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
trace_netif_receive_skb(skb);
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
orig_dev = skb->dev;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
skb_reset_network_header(skb);
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
#if !defined(CONFIG_DEBUG_NET)
빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.
/* We plan to no longer reset the transport header here.
원본 주석입니다. driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* Give some time to fuzzers and dev build to catch bugs
원본 주석입니다. driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* in network stacks.
원본 주석입니다. driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*/
원본 주석입니다. driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
if (!skb_transport_header_was_set(skb))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
skb_reset_transport_header(skb);
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
#endif
빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.
skb_reset_mac_len(skb);
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
pt_prev = NULL;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
another_round:
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
skb->skb_iif = skb->dev->ifindex;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
__this_cpu_inc(softnet_data.processed);
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (static_branch_unlikely(&generic_xdp_needed_key)) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
int ret2;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
migrate_disable();
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
ret2 = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog),
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
&skb);
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
migrate_enable();
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (ret2 != XDP_PASS) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
ret = NET_RX_DROP;
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
goto out;
공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (eth_type_vlan(skb->protocol)) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
skb = skb_vlan_untag(skb);
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
if (unlikely(!skb))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
goto out;
공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (skb_skip_tc_classify(skb))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
goto skip_classify;
공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (pfmemalloc)
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
goto skip_taps;
공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
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와 연결해 확인합니다.
list) {
driver가 만든 skb를 protocol handler와 rx_handler로 전달하는 receive core의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
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
08 · VERIFICATION
실제 장비에서 확인하는 순서
- Counter ladder
실행 외부 송신 packet 수와 PHY/MAC/DMA/netdev/socket 수를 한 표에 기록한다.
통과 기준 처음 감소하는 두 checkpoint 사이가 실제 drop 위치다.
- Ring
실행 traffic 정지 상태에서 descriptor, head/tail과 DMA address를 dump한다.
통과 기준 ownership과 producer/consumer가 모순 없이 한 방향으로 진행해야 한다.
- NAPI
실행 irq와 napi tracepoint를 함께 수집한다.
통과 기준 IRQ mask → poll → complete → unmask 순서가 보여야 한다.
- Offload
실행 RX checksum/GRO/TSO를 하나씩 끄고 결과를 비교한다.
통과 기준 문제가 사라지는 feature가 metadata 또는 segmentation 경계를 좁힌다.
- Load
실행 64B, MTU, jumbo와 burst traffic으로 ring wrap을 반복한다.
통과 기준 drop/error 증가가 queue depth와 coalescing 설정으로 설명돼야 한다.
09 · FAILURE MATRIX
처음 끊긴 경계로 원인을 좁히기
| 관측 결과 | 우선 의심할 경계 | 다음 증거 |
|---|---|---|
| MAC counter 0 | filter, 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입니다. 그보다 아래의 통과 증거와 그 지점의 실패 증거를 한 묶음으로 남겨야 수정의 효과도 검증할 수 있습니다.