netfront 분석입니다
페이지 정보
작성자 조희승 댓글 0건 조회 10,462회 작성일 12-08-17 15:05본문
netfront 분석입니다
## netfront 분석 ##
- 김환주
- 김환주
1. netif_init
MODPARM_rx_copy - backend에서 local memory로 패킷이 copy ( full virtualization default )
MODPARM_rx_flip - packet을 포함하는 page의 owner transfer ( para virtualization default )
MODPARM_rx_copy - backend에서 local memory로 패킷이 copy ( full virtualization default )
MODPARM_rx_flip - packet을 포함하는 page의 owner transfer ( para virtualization default )
register_inetaddr_notifier
notifier_chain_register - inetaddr_chain에 notifier_inetdev(.notifier_call = inetdev_notify) 를 삽입
notifier_chain_register - inetaddr_chain에 notifier_inetdev(.notifier_call = inetdev_notify) 를 삽입
xenbus_register_frontend - netfront를 xenbus에 등록
xenbus_register_driver_common
netfront->driver에 xenbus_frontend의 bus type에 대한 정보를 할당
driver_register( netfront->driver )
bus_add_driver - bus에 netfront->driver 추가
xenbus_register_driver_common
netfront->driver에 xenbus_frontend의 bus type에 대한 정보를 할당
driver_register( netfront->driver )
bus_add_driver - bus에 netfront->driver 추가
2. setup과정
backend_changed -> network_connect -> talk_to_backend -> setup_device
backend_changed는 xenbus_driver 구조체 변수의 otherend_changed 멤버 함수에 할당
otherend_changed는 어떻게 호출 되는가? -> xenbus를 살펴보자( driver/xen/xenbus/xenbus_probe.c )
-> xenbus_register_driver_common에서 netfront->driver.probe에 등록된 xenbus_dev_probe가 호출되어 watch_otherend -> ... -> xenbus_watch_path
를 통해 otherend_changed가 callback으로 등록된 watch를 xenbus watch에 추가한다.
otherend_changed는 어떻게 호출 되는가? -> xenbus를 살펴보자( driver/xen/xenbus/xenbus_probe.c )
-> xenbus_register_driver_common에서 netfront->driver.probe에 등록된 xenbus_dev_probe가 호출되어 watch_otherend -> ... -> xenbus_watch_path
를 통해 otherend_changed가 callback으로 등록된 watch를 xenbus watch에 추가한다.
3. netfront_probe
create_netdev
alloc_etherdev - eth%d로 net_device 구조체 할당, netfront_info 구조체는 priv로 설정
np = netdev_priv(netdev) , np->xbdev = dev <== xenbus_device( netfront 변수(vif)를 가리킨다. )
netif_carrier_off(netdev) - netdev->state에 __LINK_STATE_NOCARRIER 세팅
np의 각종 필드를 초기화(rx_batch(skb queue), tx/rx lock , rx_target/rx min-max target , rx_refill_timer(rx_refill_timeout함수)
tx/rx ring의 각 엔트리의 소켓 버퍼 영역 초기화(free chain) 및 grant table reference초기화
gnttab_alloc_grant_references(TX_MAX_TARGET, &np->gref_tx_head) - 모든 TX ring slot의 grant table 할당
gnttab_alloc_grant_references(RX_MAX_TARGET, &np->gref_rx_head) - 모든 RX ring slot의 grant table 할당
netdev의 각종 필드 초기화(open = network_open , hard_start_xmit = network_start_xmit , stop = network_stop , poll = netif_poll
weight = 64 , features = NETIF_F_IP_CSUM , ethtool_ops = network_ethtool_ops , class_dev.dev = &dev->dev )
np->netdev = netdev
dev->dev.driver_data = netdev_priv(netdev)
register_netdev(info->netdev);
4. network_connect
xenbus로 부터 callback함수인 backend_changed가 호출되고, 그 함수안에서 network_connect가 호출된다.
flip 모드인지 copy 모드인지 결정한다. (xenbus_scanf)
talk_to_backend(np->xbdev, np)
MAC주소를 읽어온다. ( xen_net_read_mac(dev, info->mac) )
setup_device(dev, info)
tx/rx ring의 초기화 (SHARED_RING_INIT , FRONT_RING_INIT )
rx ring에게 grant foreigh access grant 할당 (xenbus_grant_ring(dev, virt_to_mfn(rxs)) )
gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0)
이벤트 채널 할당 ( xenbus_alloc_evtchn(dev, &info->evtchn) )
MAC 주소 할당 ( memcpy(netdev->dev_addr, info->mac, ETH_ALEN); )
이벤트 채널에 인터럽트 핸들러(netif_int) 등록 ( bind_evtchn_to_irqhandler(info->evtchn, netif_int,
SA_SAMPLE_RANDOM, netdev->name, netdev); )
xenbus에 transaction을 전송(xenbus_printf)
pending된 TX packet fragment들을 discard한다. ( netif_release_tx_bufs(np) )
Rx buffer freelist와 Rx ring을 Rebuild
각 ring entry에 대해
skb = np->rx_skbs[requeue_idx] = xennet_get_rx_skb(np, i);
ref = np->grant_rx_ref[requeue_idx] = xennet_get_rx_ref(np, i);
req = RING_GET_REQUEST(&np->rx, requeue_idx);
if ( copy )
gnttab_grant_foreign_transfer_ref(
ref, np->xbdev->otherend_id,
page_to_pfn(skb_shinfo(skb)->frags->page));
else
gnttab_grant_foreign_access_ref(
ref, np->xbdev->otherend_id,
pfn_to_mfn(page_to_pfn(skb_shinfo(skb)->
frags->page)),
0);
패킷을 보내고 받을 준비가 되었으므로 캐리어 온 (netif_carrier_on(dev); )
irq와 연결된 remote 에게 notify ( notify_remote_via_irq(np->irq); )
network_tx_buf_gc(dev);
network_alloc_rx_buffers(dev); <-- 완전 어려운 함수
Rx를 위한 소켓버퍼를 미리 할당하고 받을 버퍼 또한 할당한다. refill 타이머는 주기적으로 이 버퍼를 채워준다.
5. network_open
network_alloc_rx_buffers(dev);
np->rx.sring->rsp_event = np->rx.rsp_cons + 1;
if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
netif_rx_schedule(dev);
network_alloc_rx_buffers(dev);
np->rx.sring->rsp_event = np->rx.rsp_cons + 1;
if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
netif_rx_schedule(dev);
6. netfront_info
struct netfront_info {
struct list_head list;
struct net_device *netdev;
struct net_device_stats stats;
struct netif_tx_front_ring tx; // TX front ring
struct netif_rx_front_ring rx; // RX front ring
struct netif_rx_front_ring rx; // RX front ring
spinlock_t tx_lock;
spinlock_t rx_lock;
spinlock_t rx_lock;
unsigned int evtchn, irq; // 가상 인터럽트를 받을 이벤트 체널 포트와 IRQ 번호
unsigned int copying_receiver; // 패킷 수신시 copying 모드인가? ( 0이면 flipping mode )
unsigned int copying_receiver; // 패킷 수신시 copying 모드인가? ( 0이면 flipping mode )
/* Receive-ring batched refills. */
#define RX_MIN_TARGET 8 // recv를 배치할때 minimum target
#define RX_DFL_MIN_TARGET 64 // " " default minimum target
#define RX_MAX_TARGET min_t(int, NET_RX_RING_SIZE, 256) // maximum target ( 최대 RX 링크기만큼이지만 256의 제한 )
unsigned rx_min_target, rx_max_target, rx_target;
struct sk_buff_head rx_batch; // Rx batch를 위한 소켓 버퍼 리스트 헤드
#define RX_MIN_TARGET 8 // recv를 배치할때 minimum target
#define RX_DFL_MIN_TARGET 64 // " " default minimum target
#define RX_MAX_TARGET min_t(int, NET_RX_RING_SIZE, 256) // maximum target ( 최대 RX 링크기만큼이지만 256의 제한 )
unsigned rx_min_target, rx_max_target, rx_target;
struct sk_buff_head rx_batch; // Rx batch를 위한 소켓 버퍼 리스트 헤드
struct timer_list rx_refill_timer; // Rx refill timer
/*
* {tx,rx}_skbs store outstanding skbuffs. The first entry in tx_skbs
* is an index into a chain of free entries.
*/
struct sk_buff *tx_skbs[NET_TX_RING_SIZE+1]; // 처리되지 않은 Tx skbuffs (tx의 경우 첫 엔트리는 free entry chain을 가리킴
struct sk_buff *rx_skbs[NET_RX_RING_SIZE]; // 처리되지 않은 Rx skbuffs
* {tx,rx}_skbs store outstanding skbuffs. The first entry in tx_skbs
* is an index into a chain of free entries.
*/
struct sk_buff *tx_skbs[NET_TX_RING_SIZE+1]; // 처리되지 않은 Tx skbuffs (tx의 경우 첫 엔트리는 free entry chain을 가리킴
struct sk_buff *rx_skbs[NET_RX_RING_SIZE]; // 처리되지 않은 Rx skbuffs
#define TX_MAX_TARGET min_t(int, NET_RX_RING_SIZE, 256)
grant_ref_t gref_tx_head; // Free Tx grant reference의 head
grant_ref_t grant_tx_ref[NET_TX_RING_SIZE + 1]; // Tx grant reference list
grant_ref_t gref_rx_head; // Free Rx grant reference의 head
grant_ref_t grant_rx_ref[NET_RX_RING_SIZE]; // Rx grant reference list
grant_ref_t gref_tx_head; // Free Tx grant reference의 head
grant_ref_t grant_tx_ref[NET_TX_RING_SIZE + 1]; // Tx grant reference list
grant_ref_t gref_rx_head; // Free Rx grant reference의 head
grant_ref_t grant_rx_ref[NET_RX_RING_SIZE]; // Rx grant reference list
struct xenbus_device *xbdev; // xenbus device ( netfront로 vif를 나타냄 )
int tx_ring_ref; // Tx를 위한 shared ring의 grant ref
int rx_ring_ref; // Rx를 위한 shared ring의 grant ref
u8 mac[ETH_ALEN]; // MAC address
int tx_ring_ref; // Tx를 위한 shared ring의 grant ref
int rx_ring_ref; // Rx를 위한 shared ring의 grant ref
u8 mac[ETH_ALEN]; // MAC address
unsigned long rx_pfn_array[NET_RX_RING_SIZE]; // Rx Batching(??) 을 위한 multicall interface
struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1];
struct mmu_update rx_mmu[NET_RX_RING_SIZE];
};
struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1];
struct mmu_update rx_mmu[NET_RX_RING_SIZE];
};
7. network_tx_buf_gc
np->tx.rsp_cons부터 np->tx.rsp_prod(이것은 처리중 업데이트 될수 있으므로 로컬 변수에 먼저 가져온다.) 까지 완료된 Tx에 대해
grant access를 끝내고, grant ref를 해제하며, 소켓 버퍼를 tx_skbs에 free chain에 추가하고, 메모리 해제한다.
rsp_event는 race를 막기위한 것이란다....( 더 알아볼 필요성이..)
일단 gc라는 이름은 garbage collection이 아닐까 한다. Tx에 대한 소켓 버퍼와 grant들을 해제하기 때문에...
이 함수를 호출하는 함수는 network_connect , network_start_xmit , netif_int 이므로 연결시, 패킷 전송시, 인터럽트 발생시 호출되는 것을
알수 있다.
np->tx.rsp_cons부터 np->tx.rsp_prod(이것은 처리중 업데이트 될수 있으므로 로컬 변수에 먼저 가져온다.) 까지 완료된 Tx에 대해
grant access를 끝내고, grant ref를 해제하며, 소켓 버퍼를 tx_skbs에 free chain에 추가하고, 메모리 해제한다.
rsp_event는 race를 막기위한 것이란다....( 더 알아볼 필요성이..)
일단 gc라는 이름은 garbage collection이 아닐까 한다. Tx에 대한 소켓 버퍼와 grant들을 해제하기 때문에...
이 함수를 호출하는 함수는 network_connect , network_start_xmit , netif_int 이므로 연결시, 패킷 전송시, 인터럽트 발생시 호출되는 것을
알수 있다.
8. network_alloc_rx_buffers
여러개의 skbuff를 미리 할당하여 Rx ring에 요청한다. 이때 copy모드이면 단순히 foreign access 권한만 주고,
flip모드이면 foreign transfer 권한을 준다. flip의 경우 back으로부터 소켓버퍼를 받을 것이기 때문에 현재의 버퍼는
의미가 없어진다. 그러므로 update_va_mapping을 통해 해당 vaddr의 페이지 테이블 엔트리를 0으로 채우고, xen에게
페이지를 반환하기 위해 memory_op를 통해 memory를 decrease한다.
마지막으로 np->irq를 통해 remote domain(backend를 가지고 있는 driver domain)에게 notify를 한다.(HYPERVISOR_event_channel_op)
refill이라는 것은 np->rx_batch에 미리 할당한 skbuff들을 rx ring에 request하는 것을 뜻한다. 여기서 미리 skbuff를 할당할 때는
skb_shinfo(skb)->frags[0].page 에 하나의 페이지로 할당한다. 이 페이지가 grant의 대상이 되는 것이다.
skb_shinfo(skb)->frags[0].page 에 하나의 페이지로 할당한다. 이 페이지가 grant의 대상이 되는 것이다.
9. network_start_xmit
인자로 넘어온 sk_buff를 tx ring에 request하고, foreign access를 read-only로 grant를 부여한다. 이때 전송할때 각종 NIC의 특성들을
반영할 수 있는데, Checksum / segmentation offload 기능 등의 하이레벨의 기능들을 지원할 수 있다.
인자로 넘어온 sk_buff를 tx ring에 request하고, foreign access를 read-only로 grant를 부여한다. 이때 전송할때 각종 NIC의 특성들을
반영할 수 있는데, Checksum / segmentation offload 기능 등의 하이레벨의 기능들을 지원할 수 있다.
10. netif_int
network_tx_buf_gc(dev);
/* Under tx_lock: protects access to rx shared-ring indexes. */
if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
netif_rx_schedule(dev);
network_tx_buf_gc(dev);
/* Under tx_lock: protects access to rx shared-ring indexes. */
if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
netif_rx_schedule(dev);
먼저 network_tx_buf_gc를 호출하여 tx response를 처리하고, 만약에 rx ring에 아직 consume되지 않은 response가 있으면
netif_rx_schedule을 호출하여, dev->poll_list에 dev를 삽입한다. 이것은 추후 netif_poll 함수에 의해 처리되게 된다.
netif_rx_schedule을 호출하여, dev->poll_list에 dev를 삽입한다. 이것은 추후 netif_poll 함수에 의해 처리되게 된다.
11. netif_poll
댓글목록
등록된 댓글이 없습니다.