<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                說點題外話(我不僅把這些當作技術文章,還當作工作筆記,甚至當作生活日記),最近工作在制作各種docker鏡像,有點小忙。而工作剩余時間又在看匯編,和操作系統知識,所以對ovs就沒什么時間去了解了。不過還好,這周空閑下來了,就看了下ovs中的upcall()函數調用。 話說現在ovs已經出了2.xxx版本了,我稍微瀏覽了下,發現有些函數名改變了,但其主要功能還是保留的。為了銜接前幾篇blog,所以我還是選擇下載1.xx版本的源代碼來分析。我以前那套ovs源代碼做了很多筆記,不過可惜搬公司的時候服務器壞掉了,所有數據都找不到了(因為分析這個源代碼是個人行為、私事,所以也就沒有去恢復硬盤了)。 還有個事要麻煩下,我分析這些源代碼是以個人的觀點和判斷,我沒有什么資料,就是一步一步的去分析,然后組成整個框架,其中當然免不了有些錯誤(人家是世界級團隊完成的,你一個小程序員花這么點時間就想弄明白,那估計是不太可能的),所以我非常鼓勵支持查資料的朋友僅僅是把我的分析當作一種參考,然后如果發現和我猜想的框架有問題時能及時告知我,謝謝!! 好了,下面正式談談和源代碼有關的事了。我看了下upcall()函數的大體實現,其中主線是用Linux內核中的NetLink通信機制。而其中涉及到一些其他知識點,大部分在前面已經分析過了;但vlan知識點,在前面好像基本上沒有提到,個人覺得這是個非常有價值的知識點,后續我會好好了解下。而有關ovs的前一篇[openVswitch(OVS)源代碼分析 upcall調用(之linux中的NetLink通信機制)](http://blog.csdn.net/yuzhihui_no1/article/details/40790131)我現在到回去看了看,感覺沒有寫好,有點懊惱。有些東西寫的不夠仔細,太注重代碼的實現了,而沒寫好一些理論的東西。如果要了解upcall()函數,那些基礎的結構體還是要重點了解下,所以我會修改前面的NetLink分析或者到后面再分析下理論知識。 現在來想下為什么有upcall()函數?因為比如當第一個數據包過來時(前期沒有和這個數據包的ip主機通信過),ovs中沒有任何有關于該主機的信息,更沒有設置一些規則來處理接受到該主機的數據包。所以當第一次接受到這個數據包時,就要提取出數據包中一些信息,下發到用戶空間去,讓用戶空間做些規則用來處理下次接收到的該類數據包。 下面開始看代碼,還是從void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)函數開始切入吧。 ~~~ if (unlikely(!flow)) {//查不到流表的情況 struct dp_upcall_info upcall; upcall.cmd = OVS_PACKET_CMD_MISS; //包miss,表示這個包是沒匹配到的 upcall.key = &key; //key值,對一個sk_buff網絡包的特征數據進行提取組成的結構 upcall.userdata = NULL; // 傳送給用戶空間的數據 upcall.portid = p->upcall_portid; //傳送給用戶空間時使用的id號,netlink中已經說明 ovs_dp_upcall(dp, skb, &upcall); // 調用函數處理,本blog的主角 consume_skb(skb); // 釋放掉包結構==》kfree_skb(skb) stats_counter = &stats->n_missed; //對包的計算 goto out; } ~~~ 下面就是輪到今天的主角出場了: ~~~ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, const struct dp_upcall_info *upcall_info) { struct dp_stats_percpu *stats; int dp_ifindex; int err; // 判斷下pid是否為0,這個是用來NetLink通訊使用的,為0表示傳給內核空間的 if (upcall_info->portid == 0) { err = -ENOTCONN; goto err; } // 這個字段呢,是個設備結構索引號,等下詳細分析,也是這篇blog的重點 dp_ifindex = get_dpifindex(dp); if (!dp_ifindex) { err = -ENODEV; goto err; } /* * ? ? forward_ip_summed - map internal checksum state back onto native * kernel fields. * @skb: Packet to manipulate. * @xmit: Whether we are about send on the transmit path the network stack. *?This follows the same logic as the @xmit field in compute_ip_summed(). *?Generally, a given vport will have opposite values for @xmit passed to *?these two functions. * When a packet is about to egress from OVS take our internal fields (including * any modifications we have made) and recreate the correct representation for * this kernel. ?This may do things like change the transport header offset. */ forward_ip_summed(skb, true); //下面這兩個函數就是要排隊發送信息到用戶空間的,不過這要到下一篇blog分析 if (!skb_is_gso(skb)) err = queue_userspace_packet(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info); else err = queue_gso_packets(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info); if (err) goto err; return 0; // 下面是出錯時,跳轉到這里做退出處理的,就是一些數據包的統計等操作 err: stats = this_cpu_ptr(dp->stats_percpu); u64_stats_update_begin(&stats->sync); stats->n_lost++; u64_stats_update_end(&stats->sync); return err; } ~~~ 上面是大概的分析了下upcall()函數,不過這不是本blog的重點,本blog重點是由dp_ifindex = get_dpifindex(dp);引出的一個框架問題,感覺有必要分析清楚(有這個價值),關系到網橋和端口之間的關系。 =================================================================================================================== 切入點還是從dp_ifindex = get_dpifindex(dp);開始吧,這是一個設備接口索引,就是獲取網卡設備索引號的。 ~~~ static int get_dpifindex(struct datapath *dp) { struct vport *local; int ifindex; // rcu讀鎖 rcu_read_lock(); // 根據網橋和指定port_no查找vport結構體 local = ovs_vport_rcu(dp, OVSP_LOCAL); //get_ifindex:獲取與所述設備相關聯的系統的接口索引。這個可以參考net_device網絡設備結構體 //可以為null,如果設備不具備的接口索引。 if (local) ifindex = local->ops->get_ifindex(local); else ifindex = 0; rcu_read_unlock(); return ifindex; } ~~~ 繼續追查下去,發現最后會調用struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no)函數來查詢端口結構體。其實到這里你就會發現一些情況了。其中注意下各個函數的調用傳的參數。 ~~~ struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no) { struct vport *vport; struct hlist_head *head; // 這個調用了vport_hash_bucker()函數,具體實現在下面,這是一個查找hash表頭部的函數 head = vport_hash_bucket(dp, port_no); // 上面是查找hash表頭部,說明有多個hash表頭,每個hash表頭下面應該掛載了很多node節點 // 而下面就是Linux內核中定義的宏,用來遍歷查找hash表中每個node節點的,通過匹配port_no來查找到vport // struct vport ; struct hlist_head head ; struct hlist_node hlist_for_each_entry_rcu(vport, head, dp_hash_node) { if (vport->port_no == port_no) return vport; } return NULL; } /*----------------------------------------------------------------------------------------------*/ // 這是查找哈希頭函數,有多個相連的hash頭鏈表 static struct hlist_head *vport_hash_bucket(const struct datapath *dp,</span> u16 port_no) { // port_no & (DP_VPORT_HASH_BUCKETS - 1)就是查找hash位置 // 比如表長為8的,需要查找id為10,那么用10/8 == 2。10 & (8-1) == 2 return &dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)]; } /*----------------------------------------------------------------------------------------------*/ // 下面是Linux中定義的宏,專門用來遍歷鏈表中的節點的 // struct vport ; struct hlist_head head ; struct hlist_node // hlist_for_each_entry_rcu(vport, head, dp_hash_node) #define hlist_for_each_entry_rcu(pos, head, member) \ for (pos = hlist_entry_safe (rcu_dereference_raw(hlist_first_rcu(head)),\ typeof(*(pos)), member); \ pos; \ pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu(\ &(pos)->member)), typeof(*(pos)), member)) /*----------------------------------------------------------------------------------------------*/ // xxx(first, vport , datapath) 求vport的結構體 #define hlist_entry_safe(ptr, type, member) \ ({ typeof(ptr) ____ptr = (ptr); \ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ }) ~~~ 上面的遍歷鏈表節點宏,是Linux專門定義的,個人感覺還是比較巧妙。在內核代碼中有很多地方用到這個宏,hlist_entry_safe(xxxx)就是[linux內核之container_of()詳解(即:list_entry()的詳解)](http://blog.csdn.net/yuzhihui_no1/article/details/38356393)。 分析到這里看出什么問題來了沒?就是網橋和端口連接關系問題。 第一、在struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no)中調用了vport_hash_bucket(dp, port_no);來獲取head結構體,這個函數非常簡單,可以看到它里面的實現其實就一句話:&dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)];但這說明了一個問題,就是vport的head在個哈希表中。 第二、調用hlist_entry_safe()傳的參數:hlist_entry_safe(head->first,vport,dp_hash_node),這可以看出dp_hash_node是連接head下面的,組成vport鏈表的。 所以綜合上面情況,可以看出網橋和vport的連接結構為: ![](https://box.kancloud.cn/2016-02-17_56c42ec4ef46f.jpg) 上面圖中vport結構體鏈表其實是用dp_hash_node鏈接起來的,所以說dp_hash_node是哈希鏈表鏈接元素,而其他則是數據結構體。為了形象點,所以沒怎么區分,理解就行。 看到這個結構可能會有點誘惑:那么vport中的struct?hlist_node?hash_node;字段是干什么的。開始我也以為這個字段是連接vport形成vport鏈表的,而dp_hash_node是有關網橋的鏈表。但錯了,雖然現在我也不能夠非常清楚hash_node字段是干什么用的。可以查看下vport結構體各個字段解釋: ~~~ /** * struct vport - one port within a datapath * @rcu: RCU callback head for deferred destruction. * @dp: Datapath to which this port belongs. * @upcall_portid: The Netlink port to use for packets received on this port that * miss the flow table. * @port_no: Index into @dp's @ports array. * @hash_node: Element in @dev_table hash table in vport.c. * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. * @ops: Class structure. * @percpu_stats: Points to per-CPU statistics used and maintained by vport * @stats_lock: Protects @err_stats and @offset_stats. * @err_stats: Points to error statistics used and maintained by vport * @offset_stats: Added to actual statistics as a sop to compatibility with * XAPI for Citrix XenServer. Deprecated. */ ~~~ 我追查了下hash_node,確實發現和dev_table有關,但具體的還沒有分析出來。dev_tables是什么?他的定義是: ~~~ /* Protected by RCU read lock for reading, ovs_mutex for writing. */ static struct hlist_head *dev_table; ~~~ ? ? ? 可以看下他們相關聯的函數:struct vport *ovs_vport_locate(struct net *net, const char *name),現在只找相關的東西,不會具體分析函數語句: ~~~ struct vport *ovs_vport_locate(struct net *net, const char *name) { struct hlist_head *bucket = hash_bucket(net, name); struct vport *vport; hlist_for_each_entry_rcu(vport, bucket, hash_node)// 這里可以看出vport結構體中的hash_node是和bucket一樣的,那么bucket是什么呢? if (!strcmp(name, vport->ops->get_name(vport)) && net_eq(ovs_dp_get_net(vport->dp), net)) return vport; return NULL; } ~~~ 在追查bucket時,調用了hash_bucket()函數,可以看下實現: ~~~ static struct hlist_head *hash_bucket(struct net *net, const char *name) { unsigned int hash = jhash(name, strlen(name), (unsigned long) net);// 求隨機數 return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];// 返回的是dev_table表中的某個元素的地址 } ~~~ 到這里就可以看出vport結構中的hash_node確實和dev_table有關,具體有什么關系就不再深究了,因為這不是科研。如果看了前面那副框架圖的,在網橋連接vport的框架部分應該是上面的圖了,當然這是個人觀點。 轉載請注明作者和原文出處,原文地址:[http://blog.csdn.net/yuzhihui_no1/article/details/41546481](http://blog.csdn.net/yuzhihui_no1/article/details/41546481) 若有不正確之處,望大家指正,共同學習!謝謝!!!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看