Linux Wireless架構總結

1.  無線網絡驅動(ath9k_htc)

     ath9k_htc是一個基於USB接口的SoftMAC無線網絡適配器。爲了其驅動能正常工作,首先必須調用usb_register來註冊驅動定義的usb_driver,以藉助USB Core的力量來處理與USB協議相關的事件。其代碼如下:

  1. static struct usb_driver ath9k_hif_usb_driver = {  
  2.     .name = KBUILD_MODNAME,  
  3.     .probe = ath9k_hif_usb_probe,  
  4.     .disconnect = ath9k_hif_usb_disconnect,  
  5. #ifdef CONFIG_PM  
  6.     .suspend = ath9k_hif_usb_suspend,  
  7.     .resume = ath9k_hif_usb_resume,  
  8.     .reset_resume = ath9k_hif_usb_resume,  
  9. #endif  
  10.     .id_table = ath9k_hif_usb_ids,  
  11.     .soft_unbind = 1,  
  12. };  


2. 關鍵數據結構

1) struct ieee80211_hw: 它包含802.11 PHY的配置硬件信息。



2.1 各層間關鍵數據接口





3. USB無線適配器枚舉過程 

     當此基於USB接口的無線網絡適配器被枚舉時,ath9k_hif_usb_probe將被調用。其調用流程如下圖所示:


3.1 struct ieee80211_ops 實例 ath9k_htc_ops(驅動實現)

       ath9k_htc_ops: mac80211通過這些回調函數回調driver的處理函數。ath9k_htc爲了接受mac80211的管理,它必須首先向mac80211註冊,以申明自己的存在,從而可以接受mac80211的調用。

  1. struct ieee80211_ops ath9k_htc_ops = {  
  2.     .tx                 = ath9k_htc_tx,  // 發送mac80211要求發送的幀  
  3.     .start              = ath9k_htc_start, // 第一個被attach到此硬件的net_device被enable之前被調用,之後,可以接收幀數據  
  4.     .stop               = ath9k_htc_stop,  // 最後一個被attach到此硬件的net_device被disable之後被調用,之後,不可以接收幀數據  
  5.     .add_interface      = ath9k_htc_add_interface, // 當一個被attach到此硬件的net_device被enable時被調用  
  6.     .remove_interface   = ath9k_htc_remove_interface, // 通知driver一個接口將要going down  
  7.     .config             = ath9k_htc_config,           // mac802.11調用它修改硬件配置  
  8.     .configure_filter   = ath9k_htc_configure_filter, // 配置設備的接收過濾器  
  9.     .sta_add            = ath9k_htc_sta_add,  
  10.     .sta_remove         = ath9k_htc_sta_remove,  
  11.     .conf_tx            = ath9k_htc_conf_tx,  
  12.     .bss_info_changed   = ath9k_htc_bss_info_changed,  
  13.     .set_key            = ath9k_htc_set_key,  
  14.     .get_tsf            = ath9k_htc_get_tsf,  
  15.     .set_tsf            = ath9k_htc_set_tsf,  
  16.     .reset_tsf          = ath9k_htc_reset_tsf,  
  17.     .ampdu_action       = ath9k_htc_ampdu_action,  
  18.     .sw_scan_start      = ath9k_htc_sw_scan_start,  
  19.     .sw_scan_complete   = ath9k_htc_sw_scan_complete,  
  20.     .set_rts_threshold  = ath9k_htc_set_rts_threshold,  
  21.     .rfkill_poll        = ath9k_htc_rfkill_poll_state,  
  22.     .set_coverage_class = ath9k_htc_set_coverage_class,  
  23.     .set_bitrate_mask   = ath9k_htc_set_bitrate_mask,  
  24. };  


3.2 struct cfg80211_ops 實例 mac80211_config_ops(mac80211實現)  

   cfg80211_ops定義了無線配置的操作,在它的增加虛擬接口(ieee80211_add_iface)中,它將創建並註冊net_device。在mac80211中,其定義如下所示:

  1. struct cfg80211_ops mac80211_config_ops = {  
  2.     .add_virtual_intf = ieee80211_add_iface, //使用給定的名字創建一個"虛擬接口",在wiphy的命名空間中創建net_device並返回  
  3.     .del_virtual_intf = ieee80211_del_iface, //刪除由ifindex指定的"虛擬接口"  
  4.     .change_virtual_intf = ieee80211_change_iface,  
  5.     .add_key = ieee80211_add_key,  
  6.     .del_key = ieee80211_del_key,  
  7.     .get_key = ieee80211_get_key,  
  8.     .set_default_key = ieee80211_config_default_key,  
  9.     .set_default_mgmt_key = ieee80211_config_default_mgmt_key,  
  10.     .add_beacon = ieee80211_add_beacon,  
  11.     .set_beacon = ieee80211_set_beacon,  
  12.     .del_beacon = ieee80211_del_beacon,  
  13.     .add_station = ieee80211_add_station,  
  14.     .del_station = ieee80211_del_station,  
  15.     .change_station = ieee80211_change_station,  
  16.     .get_station = ieee80211_get_station,  
  17.     .dump_station = ieee80211_dump_station,  
  18.     .dump_survey = ieee80211_dump_survey,  
  19. #ifdef CONFIG_MAC80211_MESH  
  20.     .add_mpath = ieee80211_add_mpath,  
  21.     .del_mpath = ieee80211_del_mpath,  
  22.     .change_mpath = ieee80211_change_mpath,  
  23.     .get_mpath = ieee80211_get_mpath,  
  24.     .dump_mpath = ieee80211_dump_mpath,  
  25.     .update_mesh_config = ieee80211_update_mesh_config,  
  26.     .get_mesh_config = ieee80211_get_mesh_config,  
  27.     .join_mesh = ieee80211_join_mesh,  
  28.     .leave_mesh = ieee80211_leave_mesh,  
  29. #endif  
  30.     .change_bss = ieee80211_change_bss,  
  31.     .set_txq_params = ieee80211_set_txq_params,  
  32.     .set_channel = ieee80211_set_channel,  
  33.     .suspend = ieee80211_suspend,  
  34.     .resume = ieee80211_resume,  
  35.     .scan = ieee80211_scan,  
  36.     .sched_scan_start = ieee80211_sched_scan_start,  
  37.     .sched_scan_stop = ieee80211_sched_scan_stop,  
  38.     .auth = ieee80211_auth,  
  39.     .assoc = ieee80211_assoc,  
  40.     .deauth = ieee80211_deauth,  
  41.     .disassoc = ieee80211_disassoc,  
  42.     .join_ibss = ieee80211_join_ibss,  
  43.     .leave_ibss = ieee80211_leave_ibss,  
  44.     .set_wiphy_params = ieee80211_set_wiphy_params,  
  45.     .set_tx_power = ieee80211_set_tx_power,  
  46.     .get_tx_power = ieee80211_get_tx_power,  
  47.     .set_wds_peer = ieee80211_set_wds_peer,  
  48.     .rfkill_poll = ieee80211_rfkill_poll,  
  49.     CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)  
  50.     .set_power_mgmt = ieee80211_set_power_mgmt,  
  51.     .set_bitrate_mask = ieee80211_set_bitrate_mask,  
  52.     .remain_on_channel = ieee80211_remain_on_channel,  
  53.     .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,  
  54.     .mgmt_tx = ieee80211_mgmt_tx,  
  55.     .mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,  
  56.     .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,  
  57.     .mgmt_frame_register = ieee80211_mgmt_frame_register,  
  58.     .set_antenna = ieee80211_set_antenna,  
  59.     .get_antenna = ieee80211_get_antenna,  
  60.     .set_ringparam = ieee80211_set_ringparam,  
  61.     .get_ringparam = ieee80211_get_ringparam,  
  62. }  


3.3 struct iw_handler_def  實例 cfg80211_wext_handler(wireless實現)

      cfg80211_wext_handler實現了wext要求的ioctl操作,將通過net_device->wireless_handlers->standard[ioctl cmd- SIOCIWFIRST]來進行調用。在net/wireless/wext-compat.c中的定義如下所示:

  1. static const iw_handler cfg80211_handlers[] = {  
  2.     [IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname,  
  3.     [IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq,  
  4.     [IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq,  
  5.     [IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode,  
  6.     [IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode,  
  7.     [IW_IOCTL_IDX(SIOCGIWRANGE)]    = (iw_handler) cfg80211_wext_giwrange,  
  8.     [IW_IOCTL_IDX(SIOCSIWAP)]   = (iw_handler) cfg80211_wext_siwap,  
  9.     [IW_IOCTL_IDX(SIOCGIWAP)]   = (iw_handler) cfg80211_wext_giwap,  
  10.     [IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme,  
  11.     [IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan,  
  12.     [IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan,  
  13.     [IW_IOCTL_IDX(SIOCSIWESSID)]    = (iw_handler) cfg80211_wext_siwessid,  
  14.     [IW_IOCTL_IDX(SIOCGIWESSID)]    = (iw_handler) cfg80211_wext_giwessid,  
  15.     [IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate,  
  16.     [IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate,  
  17.     [IW_IOCTL_IDX(SIOCSIWRTS)]  = (iw_handler) cfg80211_wext_siwrts,  
  18.     [IW_IOCTL_IDX(SIOCGIWRTS)]  = (iw_handler) cfg80211_wext_giwrts,  
  19.     [IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag,  
  20.     [IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag,  
  21.     [IW_IOCTL_IDX(SIOCSIWTXPOW)]    = (iw_handler) cfg80211_wext_siwtxpower,  
  22.     [IW_IOCTL_IDX(SIOCGIWTXPOW)]    = (iw_handler) cfg80211_wext_giwtxpower,  
  23.     [IW_IOCTL_IDX(SIOCSIWRETRY)]    = (iw_handler) cfg80211_wext_siwretry,  
  24.     [IW_IOCTL_IDX(SIOCGIWRETRY)]    = (iw_handler) cfg80211_wext_giwretry,  
  25.     [IW_IOCTL_IDX(SIOCSIWENCODE)]   = (iw_handler) cfg80211_wext_siwencode,  
  26.     [IW_IOCTL_IDX(SIOCGIWENCODE)]   = (iw_handler) cfg80211_wext_giwencode,  
  27.     [IW_IOCTL_IDX(SIOCSIWPOWER)]    = (iw_handler) cfg80211_wext_siwpower,  
  28.     [IW_IOCTL_IDX(SIOCGIWPOWER)]    = (iw_handler) cfg80211_wext_giwpower,  
  29.     [IW_IOCTL_IDX(SIOCSIWGENIE)]    = (iw_handler) cfg80211_wext_siwgenie,  
  30.     [IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth,  
  31.     [IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth,  
  32.     [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext,  
  33.     [IW_IOCTL_IDX(SIOCSIWPMKSA)]    = (iw_handler) cfg80211_wext_siwpmksa,  
  34.   [IW_IOCTL_IDX(SIOCSIWPRIV)] = (iw_handler)cfg80211_wext_setpriv  
  35. };  
  36.   
  37. const struct iw_handler_def cfg80211_wext_handler = {  
  38.     .num_standard       = ARRAY_SIZE(cfg80211_handlers),  
  39.     .standard       = cfg80211_handlers,  
  40.     .get_wireless_stats = cfg80211_wireless_stats,  
  41. }  


4. 創建並註冊net_device

             當執行mac80211_config_ops-> ieee80211_add_iface時,它將創建net_device和對應的ieee80211_sub_if_data, 然後主要做了以下幾件事:

        1) 把net_device對應的名字增加到/sys/class/net/目錄下   

        2) 把新創建的net_device插入到init_net->dev_base_head中

        3) 通知上層協議,有一個新的net_device出現了,大家可以使用它了

        4) 把新創建的ieee80211_sub_if_data增加到ieee80211_local的interfaces列表中        

        其流程如下圖所示:


        mac80211中定義的net_device_ops ieee80211_dataif_ops,以下這些方法,都有一個struct net_device參數。其具體定義如下:   

  1. static const struct net_device_ops ieee80211_dataif_ops = {  
  2.     .ndo_open       = ieee80211_open,              // net_device變換到 UP 時被調用  
  3.     .ndo_stop       = ieee80211_stop,              // net_device變換到 Down 時被調用  
  4.     .ndo_uninit     = ieee80211_teardown_sdata,    // 取消註冊或註冊失敗時調用  
  5.     .ndo_start_xmit     = ieee80211_subif_start_xmit,  // 需要發送包時被調用  
  6.     .ndo_set_multicast_list = ieee80211_set_multicast_list,// 多播地址列表變化時被調用  
  7.     .ndo_change_mtu     = ieee80211_change_mtu,        // 當用戶想改變一個設備的MTU時被調用  
  8.     .ndo_set_mac_address    = ieee80211_change_mac,        // mac地址需要改變時被調用  
  9.     .ndo_select_queue   = ieee80211_netdev_select_queue, //當net_device支持多個發送隊列時,用來決定使用哪個隊列  
  10. };  

  mac80211中初始化net_device->netdev_ops:

  1. static void ieee80211_if_setup(struct net_device *dev)  
  2. {  
  3.     ether_setup(dev);  
  4.     dev->priv_flags &= ~IFF_TX_SKB_SHARING;  
  5.     dev->netdev_ops = &ieee80211_dataif_ops;  
  6.     dev->destructor = free_netdev;  
  7. }  


5. 數據接收(Data RX)流程

   數據接收流程如下圖所示:


IP層與TCP/UDP層接口定義如下:

  1. static const struct net_protocol tcp_protocol = {  
  2.     .handler =  tcp_v4_rcv,  
  3.     .err_handler =  tcp_v4_err,  
  4.     .gso_send_check = tcp_v4_gso_send_check,  
  5.     .gso_segment =  tcp_tso_segment,  
  6.     .gro_receive =  tcp4_gro_receive,  
  7.     .gro_complete = tcp4_gro_complete,  
  8.     .no_policy =    1,  
  9.     .netns_ok = 1,  
  10. };  
  11.   
  12. static const struct net_protocol udp_protocol = {  
  13.     .handler =  udp_rcv,  
  14.     .err_handler =  udp_err,  
  15.     .gso_send_check = udp4_ufo_send_check,  
  16.     .gso_segment = udp4_ufo_fragment,  
  17.     .no_policy =    1,  
  18.     .netns_ok = 1,  
  19. };  
  20.   
  21. static const struct net_protocol icmp_protocol = {  
  22.     .handler =  icmp_rcv,  
  23.     .err_handler =  ping_err,  
  24.     .no_policy =    1,  
  25.     .netns_ok = 1,  
  26. };  

IP層與net/core層接口定義如下:

  1. static struct packet_type ip_packet_type __read_mostly = {  
  2.     .type = cpu_to_be16(ETH_P_IP),  
  3.     .func = ip_rcv,  
  4.     .gso_send_check = inet_gso_send_check,  
  5.     .gso_segment = inet_gso_segment,  
  6.     .gro_receive = inet_gro_receive,  
  7.     .gro_complete = inet_gro_complete,  
  8. };  




接收下半部stack如下所示:

  1. [  336.646628] [<c07d8b24>] (tcp_rcv_established+0x648/0x9b0) from [<c07e04cc>] (tcp_v4_do_rcv+0x74/0x2a8)  
  2. [  336.646661] [<c07e04cc>] (tcp_v4_do_rcv+0x74/0x2a8) from [<c07e0c40>] (tcp_v4_rcv+0x540/0x908)  
  3. [  336.646678] [<c07e0c40>] (tcp_v4_rcv+0x540/0x908) from [<c07c23d4>] (ip_local_deliver_finish+0x158/0x318)  
  4. [  336.646694] [<c07c23d4>] (ip_local_deliver_finish+0x158/0x318) from [<c07c1e44>] (ip_rcv_finish+0x420/0x440)  
  5. [  336.646715] [<c07c1e44>] (ip_rcv_finish+0x420/0x440) from [<c0772dcc>] (__netif_receive_skb+0x4d0/0x534)  
  6. [  336.646730] [<c0772dcc>] (__netif_receive_skb+0x4d0/0x534) from [<c0774434>] (netif_receive_skb+0x9c/0xb4)  
  7. [  336.646752] [<c0774434>] (netif_receive_skb+0x9c/0xb4) from [<c08b8e6c>] (ieee80211_deliver_skb+0x134/0x164)  
  8. [  336.646769] [<c08b8e6c>] (ieee80211_deliver_skb+0x134/0x164) from [<c08b9ed8>] (ieee80211_rx_handlers+0x103c/0x1978)  
  9. [  336.646785] [<c08b9ed8>] (ieee80211_rx_handlers+0x103c/0x1978) from [<c08baf10>] (ieee80211_prepare_and_rx_handle+0x6fc/0x788)  
  10. [  336.646802] [<c08baf10>] (ieee80211_prepare_and_rx_handle+0x6fc/0x788) from [<c08bb920>] (ieee80211_rx+0x908/0x988)  
  11. [  336.646819] [<c08bb920>] (ieee80211_rx+0x908/0x988) from [<c068a578>] (ath9k_rx_tasklet+0x4e4/0x54c)  
  12. [  336.646835] [<c068a578>] (ath9k_rx_tasklet+0x4e4/0x54c) from [<c0467d98>] (tasklet_action+0xa8/0x14c)  
  13. [  336.646850] [<c0467d98>] (tasklet_action+0xa8/0x14c) from [<c0468144>] (__do_softirq+0x88/0x158)  
  14. [  336.646863] [<c0468144>] (__do_softirq+0x88/0x158) from [<c0468414>] (irq_exit+0x48/0xac)  
  15. [  336.646882] [<c0468414>] (irq_exit+0x48/0xac) from [<c04313c0>] (do_local_timer+0x50/0x80)  
  16. [  336.646898] [<c04313c0>] (do_local_timer+0x50/0x80) from [<c0436708>] (__irq_svc+0x48/0xe0)  


6. 數據發送(Data TX)流珵

   數據發送流程如下圖所示:



上半部分涉及到的相關代碼如下所示(以上流程主要通過dump_stack獲取):

     net/socket.c
     net/ipv4/af_net.c
     net/ipv4/tcp.c
     net/ipv4/tcp_output.c
     net/ipv4/ip_output.c
     net/core/neighbour.c
     net/core/dev.c


7. INET初始化

    INET爲Linux OS實現了TCP/IP協議集,它使用BSD Socket接口作爲與User通訊的方式。其初始化代碼如下所示:

    代碼位於:net/ipv4/af_inet.c。

  1. static int __init inet_init(void)  
  2. {  
  3.     struct sk_buff *dummy_skb;  
  4.     struct inet_protosw *q;  
  5.     struct list_head *r;  
  6.     int rc = -EINVAL;  
  7.   
  8.     BUILD_BUG_ON(sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb));  
  9.   
  10.     sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);  
  11.     if (!sysctl_local_reserved_ports)  
  12.         goto out;  
  13.   
  14.     rc = proto_register(&tcp_prot, 1);  
  15.     if (rc)  
  16.         goto out_free_reserved_ports;  
  17.   
  18.     rc = proto_register(&udp_prot, 1);  
  19.     if (rc)  
  20.         goto out_unregister_tcp_proto;  
  21.   
  22.     rc = proto_register(&raw_prot, 1);  
  23.     if (rc)  
  24.         goto out_unregister_udp_proto;  
  25.   
  26.     rc = proto_register(&ping_prot, 1);  
  27.     if (rc)  
  28.         goto out_unregister_raw_proto;  
  29.   
  30.     /* 
  31.      *  Tell SOCKET that we are alive... 
  32.      */  
  33.   
  34.     (void)sock_register(&inet_family_ops);  
  35.   
  36. #ifdef CONFIG_SYSCTL  
  37.     ip_static_sysctl_init();  
  38. #endif  
  39.   
  40.     /* 
  41.      *  Add all the base protocols. 
  42.      */  
  43.   
  44.     if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)  
  45.         printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");  
  46.     if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)  
  47.         printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");  
  48.     if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)  
  49.         printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");  
  50. #ifdef CONFIG_IP_MULTICAST  
  51.     if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)  
  52.         printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");  
  53. #endif  
  54.   
  55.     /* Register the socket-side information for inet_create. */  
  56.     for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)  
  57.         INIT_LIST_HEAD(r);  
  58.   
  59.     for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)  
  60.         inet_register_protosw(q);  
  61.   
  62.     /* 
  63.      *  Set the ARP module up 
  64.      */  
  65.   
  66.     arp_init();  
  67.   
  68.     /* 
  69.      *  Set the IP module up 
  70.      */  
  71.   
  72.     ip_init();  
  73.   
  74.     tcp_v4_init();  
  75.   
  76.     /* Setup TCP slab cache for open requests. */  
  77.     tcp_init();  
  78.   
  79.     /* Setup UDP memory threshold */  
  80.     udp_init();  
  81.   
  82.     /* Add UDP-Lite (RFC 3828) */  
  83.     udplite4_register();  
  84.   
  85.     ping_init();  
  86.   
  87.     /* 
  88.      *  Set the ICMP layer up 
  89.      */  
  90.   
  91.     if (icmp_init() < 0)  
  92.         panic("Failed to create the ICMP control socket.\n");  
  93.   
  94.     /* 
  95.      *  Initialise the multicast router 
  96.      */  
  97. #if defined(CONFIG_IP_MROUTE)  
  98.     if (ip_mr_init())  
  99.         printk(KERN_CRIT "inet_init: Cannot init ipv4 mroute\n");  
  100. #endif  
  101.     /* 
  102.      *  Initialise per-cpu ipv4 mibs 
  103.      */  
  104.   
  105.     if (init_ipv4_mibs())  
  106.         printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n");  
  107.   
  108.     ipv4_proc_init();  
  109.   
  110.     ipfrag_init();  
  111.   
  112.     dev_add_pack(&ip_packet_type);  
  113.   
  114.     rc = 0;  
  115. out:  
  116.     return rc;  
  117. out_unregister_raw_proto:  
  118.     proto_unregister(&raw_prot);  
  119. out_unregister_udp_proto:  
  120.     proto_unregister(&udp_prot);  
  121. out_unregister_tcp_proto:  
  122.     proto_unregister(&tcp_prot);  
  123. out_free_reserved_ports:  
  124.     kfree(sysctl_local_reserved_ports);  
  125.     goto out;  
  126. }  

發佈了3 篇原創文章 · 獲贊 11 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章