Linux Network Device Driver
NIC(Network Interface Card)
- Network Adapter라고도 불린다.(= Hardware)
- 실제 하드웨어이고, 커널에서 표현하는 data structure가 struct net_device라고 한다.
- 하드웨어를 sw적으로 구동해주기 위해 Network Device Drivers가 있다.
1. NIC
* Network Adapter
- data-signal conversion을 한다. (wireless, token-ring 등에 맞게 신호를 만들어 준다.) -> 전기 신호
- shared media의 경우 처리를 할 수 있다.(Ethernet 헤더의 preamble : 두 개의 호스트가 동시에 보낼 때 충돌인지를 판단하는 시간 : 하나의 호스트가 보내면 preamble 시간 이후에 다른 호스트가 보내야 함) -> H/W적으로 처리.
* Network Devices (= Device layer)
- 제어하기 위한 Abstraction, interface(data structure)
* Device Driver
- H/W마다 다 다르다.
2. Network Adapter
* low-level address(Ethernet의 경우 MAC)을 가지고 패킷을 판단하는 시스템
* 동의어 : Network Interface Controller, Network Interface Card, NIC, LAN adapter, LAN card
3. Network Adapter Interconnection
* motherboard로의 연결
- chipset에 포함되어 있고, PCI, PCIE bus(I/O버스에 해당)를 통해 연결되어 있다.
4. Network Devices
* 정의 : protocol(SW) 과 network adapter(HW) 사이의 interface
asynchronous input and output
* file system으로 abstract이 안 된다.
다른 디바이스들과 달리 read-write operation으로 될 수 없다.
5. net_device structure
* 디바이스(어댑터 자체)를 나타내는 구조체. (network stack까지 내려올 때는 sk_buff 구조체를 기반으로 패킷 처리)
어댑터를 제어하기 위한 함수도 가지고 있다.
6. Network Device Driver
* Implements vendor specific packet processing : 드라이버는 표준화할 수 없다. net_device가 표준화되어있고 벤더별로 처리하기 위한 드라이버의 함수들은 H/W dependent하다. 커널의 입장에서는 함수를 hook해서 사용.
* Included in the drivers/net directory
* Network Devices(dev.c와 net_device) : adapter-independent
그 아래에 Device Driver(driver.c) : adapter-specific 에서 실제적인 H/W를 동작시킨다.
7. Driver<-> Network Adapter Interface
* Drivers -> device
- memory-mapped control status registers에 명령어를 write한다.
- NIC은 그 값에 따라 동작하도록 H/W적으로 설계되어있다.
- 송신/수신을 위해 만들어진 buffer를 set한다. (buffer descriptor ring으로 만들어졌다)
- sk_buff에서 쓰는 큐와 driver가 패킷을 보내기 위한 ring은 별도로 관리된다. (전자는 protocol layer에서 후자는 디바이스 드라이버에서 관리)
* Device -> driver
- control status register에 값을 쓴다.
- buffer에서 패킷 처리는 DMA로 동작(디바이스는 CPU와 별도로 동작)
- DMA가 끝나면 generate interrupt
- descriptor ring에 상태를 업데이트(커널 layer로 패킷이 올라갔으므로 ring에서 빠짐)
8. NIC IRQ
* NIC은 interrupt와 밀접한 관련이 있다. packet을 송신/수신 시 interrupt를 발생시키기 때문이다.
* 디바이스 초기화 시 인터럽트 핸들러를 irq에 등록한다.(request_irq)
- frame 도착 시 인터럽트 핸들러가 불린다.
- transmission complete, transmission error 등의 다른 이유들로 동일한 핸들러가 불린다.
- 해제 함수(free_irq)
9. Packet Descriptor Rings
* main 메모리와 DMA 엔진 간의 패킷 통신에 쓰인다.
* physical location, length of the data, extra control and status information
Network Process Contexts from/to Devices
* 크게 두 가지로 쪼개었다.
- Top half : 짧고 간단하게, 인터럽트에 해당하는 sw 인터럽트를 스케줄한다. 받는 패킷이 들어간 링버퍼를 soft irq에 넘겨준다. -> ISR(Interrupt Service Routine)
- Bottom half :Top half에서 받은 것으로 실제적인 operation을 처리한다. -> softirqs, tasklets, workqueues
1. Interrupt Handling Philosophy in Kernel
* interrupt handler에서 최소의 동작. non-critical한 것들은 나중으로 미루자. -> 패킷하나당 인터럽트가 생기므로 handler가 길어지면 패킷드랍이 발생할 수도 있다.
2. Top Half
* Perform minimal, common functions
- unmask interrupt, flag reset 등 간단한 세팅만 한다. soft inturrupt를 schedule(interrupt가 schedule 가능하다는 것은 바로 실행 안되도 된다는 것)
- It preempts other tasks ( S/W interrupt도 preempt 하기 때문에 Top Half 가 많아지면 dos가 늘어난다)
3. Bottom Half
* Mechanisms to defer work later
- softirqs
- tasklets (built on top of softirqs)
- work queues
* All can be interrupted
- H/W interrupt가 들어올 수가 있음.
* Network transmission and reception is dealt with softirq
* SoftIRQs
- Statically allocated (at kernel compile time)
- Can be executed simultaneously on multiple CPUs
- 먼저 스케줄
* Tasklets
- 여러 IRQ의 인스턴스를 묶는 것
* Work queue
- 낮은 priority
4. Softirq
- code must be re-entrant(시작할 때 stateless, local variable로만 동작
* statically allocated
- priority가 높은 것이 preempt 한다.
- 대부분 시스템이 HI > timer > net tx > net rx > block devices 순임. cf) HI는 전원 버튼 같은 것
5. When do softirqs run
* Run at various points by the kernel
- After interrupts (top halves/IRQs)
- When the scheduler runs ksoftirqd
softirq has high priority than user context
-
6. Why SoftIRQ
* 인터럽트가 없다면 device lock(net_device 구조체 데이터를 얻는 것)을 얻는 시간이 걸린다.
* DMA buffer -> Kernel buffer -> Protocol stack을 거쳐 -> Socket으로 보내지는 것은 Interrupt Handler에서 일어난다.
7. Multi-queue NIC
* NIC에서 CPU에 할당된 RX Queue로 나뉘어 들어간다.
* RSS(receive side scaling) : 패킷을 H/W 적으로 Hash function을 이용해 나눠줌.
8. LSO & LRO
Large Receive Offload :
Large Segment Offload :
Network Stack에서 쪼개지말고 NIC에서 H/W적으로 쪼갠다. (성능을 높이기 위해서) 받을 때는 패킷을 뭉쳐서 위로 올려준다. encapsulation, decapsulation에 대한 overload가 줄어든다.
9. Checksum Offloading, Promiscuous mode
Checksum Offloading : checksum 계산을 NIC에서 H/W적으로 하자. Network stack의 overload 감소
Promiscuous mode : 나한테 오는 패킷뿐만아니라 모든 패킷을 받자. packet sniffing을 위해 사용된다.