首页 > 代码库 > OVS流表查询过程分析
OVS流表查询过程分析
OVS中流表操作的理解关键在于这里哈希表的实现,引入的 flex_array方便了内存的管理,通过 hash&(桶数-1)可以随机的将一个元素定位到某一个桶中。
接下来是代码细节。
一. 核心数据结构
//流表
struct flow_table{
struct flex_array * buckets; //具体的流表项
unsigned int count , n_buckets ; //
struct rcu_head rcu;
int node_ver;
//node_ver的存在使得我们可以控制sw_flow的哪个hlist_node 链入到这个bucket中
//但是仍然没有体会这样做的巧妙之处?
u32 hash_seed ; //哈希算法需要的种子
bool keep_flows ; //是否保留该流表项
};
//流表项
struct sw_flow{
struct rcu_head rcu;
struct hlist_node hash_node [2]; //通过其一链入所在桶的链表
u32 hash ; //存hash值
struct sw_flow_key key; //
struct sw_flow_actions __rcu *sf_acts;
spinlock_t lock ; /* Lock for values below. */
unsigned long used ;/* Last used time (in jiffies). */
u64 packet_count ; /* Number of packets matched. */
u64 byte_count ; /* Number of bytes matched. */
u8 tcp_flags ; /* Union of seen TCP flags. */
};
//弹性数组??用于大量需要数组类型的时候
struct flex_array{
union {
struct {
int element_size ;//每个元素尺寸
int total_nr_elements ; //元素个数
int elems_per_part ; //每个部分元素的个数
u32 reciprocal_elems;
struct flex_array_part *parts []; //可以有多个部分
};
//这个的组织可以使得flex_array 的大小刚好是一个物理页PAGE_SIZE
char padding [FLEX_ARRAY_BASE_SIZE ];
};
};
//flex_array 中的每个块也是物理页大小的空间
struct flex_array_part{
char elements[ FLEX_ARRAY_PART_SIZE];
};
二. 代码分析
1.先看看flow_table的初始化,可以看出相应数据结构的作用。
//构建一个flow_table,有 new_size个buckets
struct flow_table *ovs_flow_tbl_alloc( int new_size)
{
struct flow_table *table = kmalloc( sizeof (*table), GFP_KERNEL);
if (!table)
return NULL;
table-> buckets = alloc_buckets(new_size);
if (!table-> buckets) {
kfree(table);
return NULL;
}
table-> n_buckets = new_size;
table-> count = 0;
table-> node_ver = 0;
table-> keep_flows = false;
get_random_bytes(&table-> hash_seed , sizeof (u32));
return table;
}
2.接下来看datapath中比较核心的查询流表操作的细节。
//根据key来进行流表查询
struct sw_flow *ovs_flow_tbl_lookup( struct flow_table *table,
struct sw_flow_key *key, int key_len)
{
struct sw_flow *flow;
struct hlist_node *n;
struct hlist_head *head;
u8 *_key;
int key_start;
u32 hash;
//因为sw_flow_key中开始有tunnel相关字段,所以需要知道key的开始地址
key_start = flow_key_start(key);
//jhash2算法
hash = ovs_flow_hash(key, key_start, key_len);
_key = ( u8 *) key + key_start;
//根据hash值找到相应的桶,
head = find_bucket(table, hash);
//
hlist_for_each_entry_rcu(flow, n, head, hash_node[table-> node_ver]) {
if (flow->hash == hash &&
!memcmp(( u8 *)&flow-> key + key_start, _key, key_len - key_start)) {
return flow;
}
}
return NULL;
}
//通过hash值找到所在bucket的头指针
static struct hlist_head *find_bucket (struct flow_table *table, u32 hash)
{
//因为hash本来就是一个字(u32)
hash = jhash_1word(hash, table-> hash_seed);
return flex_array_get(table-> buckets,(hash & (table-> n_buckets - 1)));
}
//返回弹性数组中 element_nr号元素指针
void *flex_array_get( struct flex_array *fa, unsigned int element_nr)
{
int part_nr = 0;
struct flex_array_part *part;
if (!fa-> element_size)
return NULL;
if (element_nr >= fa-> total_nr_elements)
return NULL;
//如果当前页够容纳这些元素的话,内存区域仍然就使用parts[0]
if (elements_fit_in_base(fa))
part = ( struct flex_array_part *)&fa->parts[0];
else {
part_nr = fa_element_to_part_nr(fa, element_nr);
part = fa-> parts[part_nr];
if (!part)
return NULL;
}
return &part-> elements[index_inside_part(fa, element_nr, part_nr)];
}
//在基本数据结构flex_array中除去元数据之后,所剩的空间
//也就是当前page 除去前面几个基本字段,看剩余的空间是否够存储当前元素
static inline int elements_fit_in_base (struct flex_array *fa)
{
int data_size = fa-> element_size * fa->total_nr_elements;
if (data_size <= FLEX_ARRAY_BASE_BYTES_LEFT)
return 1;
return 0;
}
#define FLEX_ARRAY_BASE_BYTES_LEFT \
(FLEX_ARRAY_BASE_SIZE - offsetof( struct flex_array, parts))
//这里的倒数除只是定义了一种机制,能随机从元素号映射到flex_array的part number;
static int fa_element_to_part_nr (struct flex_array *fa,
unsigned int element_nr)
{
return reciprocal_divide(element_nr, fa->reciprocal_elems);
}
static inline u32 reciprocal_divide(u32 A, u32 R)
{
return ( u32)(((u64)A * R) >> 32);
}
//求得这个元素在这个part中的索引号
static unsigned int index_inside_part (struct flex_array *fa,
unsigned int element_nr,
unsigned int part_nr)
{
unsigned int part_offset;
part_offset = element_nr - part_nr * fa->elems_per_part;
return part_offset * fa-> element_size;
}
find_bucket执行完成后,最终返回该hash值对应的元素在flex_array中的地址,当然在流表中元素指的就是sw_flow。
三. 相关知识点
1.使用的是linux内核中的 jhash2 哈希算法;
110 /* jhash2 - hash an array of u32‘s
111 * @k: the key which must be an array of u32‘s
112 * @length: the number of u32‘s in the key
113 * @initval: the previous hash, or an arbitray value
114 *
115 * Returns the hash value of the key.
116 */
117 static inline u32 jhash2(const u32 *k, u32 length, u32 initval)
118 {
119 u32 a, b, c;
120
121 /* Set up the internal state */
122 a = b = c = JHASH_INITVAL + (length<<2) + initval;
123
124 /* Handle most of the key */
125 while (length > 3) {
126 a += k[0];
127 b += k[1];
128 c += k[2];
129 __jhash_mix(a, b, c);
130 length -= 3;
131 k += 3;
132 }
133
134 /* Handle the last 3 u32‘s: all the case statements fall through */
135 switch (length) {
136 case 3: c += k[2];
137 case 2: b += k[1];
138 case 1: a += k[0];
139 __jhash_final(a, b, c);
140 case 0: /* Nothing left to add */
141 break;
142 }
143
144 return c;
145 }
148 /* jhash_3words - hash exactly 3, 2 or 1 word(s) */
149 static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
150 {
151 a += JHASH_INITVAL;
152 b += JHASH_INITVAL;
153 c += initval;
154
155 __jhash_final(a, b, c);
156
157 return c;
158 }
159
160 static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
161 {
162 return jhash_3words(a, b, 0, initval);
163 }
164
165 static inline u32 jhash_1word(u32 a, u32 initval)
166 {
167 return jhash_3words(a, 0, 0, initval);
168 }
2. 巩固内核中hlist遍历原理。
转载注明出处:http://blog.csdn.net/vonzhoufz/article/details/36005667。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。