您的位置:首页 > 其它

OVS流表查询过程分析

2014-06-30 15:54 99 查看
OVS中流表操作的理解关键在于这里哈希表的实现,引入的 flex_array方便了内存的管理,通过 hash&(桶数-1)可以随机的将一个元素定位到某一个桶中。
接下来是代码细节。


一. 核心数据结构

//流表
struct flow_table{
struct flex_array
* buckets; //具体的流表项
unsigned int count , n_buckets ; //
struct rcu_head rcu;
int node_ver;
//之前的理解有误,flow
table rehash 需要。
u32 hash_seed ; //哈希算法需要的种子
bool keep_flows ; //是否保留该流表项
};
//流表项
struct sw_flow{
struct rcu_head rcu;
struct hlist_node hash_node [2];
//rehash需要
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 []; //(GNU
C支持0长度数组)可以有多个部分
};
//这个的组织可以使得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遍历原理(见hlist)。

转载注明出处:/article/1377574.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: