openVswitch(OVS)原始碼分析--FlexArray
阿新 • • 發佈:2018-12-15
FlexArray 是什麼
FlexArray
是ovs
中核心模組openvswitch.ko
中使用了一種資料結構, 意為靈活陣列(Flexible Array
)
FlexArray 定義
#define FLEX_ARRAY_BASE_SIZE PAGE_SIZE
#define FLEX_ARRAY_PART_SIZE PAGE_SIZE
struct flex_array {
union {
struct {
int element_size;
int total_nr_elements;
int elems_per_part;
struct reciprocal_value reciprocal_elems;
struct flex_array_part *parts[];
};
char padding[FLEX_ARRAY_BASE_SIZE];
};
}
struct flex_array_part {
char elements[FLEX_ARRAY_PART_SIZE];
}
FlexArray
定義如上, 它是一個union
, 其中第二項 padding 可以使整個union
結構大小固定為FLEX_ARRAY_BASE_SIZE
struct
中的欄位意義如下:
- element_size -
FlexArray
中存放的每個元素的大小.FlexArray
每個元素大小是相同的, 在建立FlexArray
時確定 - total_nr_elements -
FlexArray
中可以存放的元素的最大個數. 儘管是靈活陣列, 但這個值在建立FlexArray
時確定,並且使用過程中不能改變. - elems_per_part - 每個part中所能存放的元素的個數.這是一個計算值, 在建立
FlexArray
時確定, 一個part的大小為PAGE_SIZE, 比如在PAGE_SIZE = 4096 時, 如果要存放的元素大小為 256 Bytes, 則 elems_per_part - reciprocal_elems - 它表示 elems_per_part 的倒數, 即 1/elems_per_part . 這並不是說它會以浮點數的形式存放,而是在計算以elems_per_part 為除數的除法時, 可以讓機器計算地更有效率一點.
- parts - 這是一個指標陣列. 陣列長度為空, 這種定義方式參見GNU中的零長度陣列, 每一個指標指向一個part (額外的空間), 元素存放在part中. 注意,
FlexArray
還具有一個特性, 當 ( 元素最大個數 * 元素大小 <= 限定值 ) 時,FlexArray
將不再使用額外的part空間, 轉而使用FlexArray
自己的空間.
使用中的FlexArray
的空間結構如下圖所示
FlexArray 使用
Array 建立
struct flex_array *rpl_flex_array_alloc(int element_size, unsigned int total, gfp_t flags)
{
struct flex_array *ret;
struct reciprocal_value reciprocal_elems = {0};
int elems_per_part = 0;
int max_size = 0;
if (element_size) {
elems_per_part = FLEX_ARRAY_ELEMENTS_PER_PART(element_size); // 每一個part可以存放多少 element
reciprocal_elems = reciprocal_value(elems_per_part); // 計算elems_per_part 的倒數
max_size = FLEX_ARRAY_NR_BASE_PTRS * elems_per_part;
}
/* max_size will end up 0 if element_size > PAGE_SIZE */
if (total > max_size)
return NULL;
ret = kzalloc(sizeof(struct flex_array), flags);
if (!ret)
return NULL;
ret->element_size = element_size;
ret->total_nr_elements = total;
ret->elems_per_part = elems_per_part;
ret->reciprocal_elems = reciprocal_elems;
if (elements_fit_in_base(ret) && !(flags & __GFP_ZERO)) // elements_fit_in_base 返回是否使用flex_array 本身就夠了
memset(&ret->parts[0], FLEX_ARRAY_FREE,
FLEX_ARRAY_BASE_BYTES_LEFT);
return ret;
}
以上為FlexArray
建立的程式碼, 主要包括申請FlexArray
佔用記憶體和初始化結構中的引數,入參為要存放的每個元素大小和需要容納的元素個數. 注意, 這裡不會去申請part佔用的額外的空間, part空間是需要時才申請.
Array 存放元素
/*
向 @fa的 @element_nr位置, 存入一個元素, 該元素首地址在 @src
*/
int rpl_flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,
gfp_t flags)
{
int part_nr = 0;
struct flex_array_part *part;
void *dst;
if (element_nr >= fa->total_nr_elements) // 是否超過 array的最大容量
return -ENOSPC;
if (!fa->element_size)
return 0;
if (elements_fit_in_base(fa)) // fa是否支援使用額外的空間
part = (struct flex_array_part *)&fa->parts[0];
else {
part_nr = fa_element_to_part_nr(fa, element_nr); // 根據入參, 計算它要存放的part編號
part = __fa_get_part(fa, part_nr, flags); // 得到part空間指標, part不存在就建立
if (!part)
return -ENOMEM;
}
dst = &part->elements[index_inside_part(fa, element_nr, part_nr)]; // 得到元素目的地址
memcpy(dst, src, fa->element_size);
return 0;
}
以上為向FlexArray
存入元素的程式碼 , 需要注意入參element_nr為存入的位置編號. 舉個例子, 假設元素大小為256, PAGE_SIZE為 4096, 那麼一個part可容納16個元素, part 0包含元素範圍是0~15, part 1包含元素範圍是 16~31, 以此類推.如果向編號17的位置存入元素, 則自然會找到 part 1的對應位置.
Array 讀取元素
讀取元素在rpl_flex_array_get中實現, 比較簡單, 同樣是計算位置後取出
Array 清除元素
#define FLEX_ARRAY_FREE 0x6c
int rpl_flex_array_clear(struct flex_array *fa, unsigned int element_nr)
{
......
dst = &part->elements[index_inside_part(fa, element_nr, part_nr)]; // 得到元素目的地址
memset(dst, FLEX_ARRAY_FREE, fa->element_size);
}
清除元素在rpl_flex_array_clear中實現, 前面部分如存入元素類似. 差別在最後, 清除元素,即把元素所佔用的空間全部寫為 0x6c
Array 收縮
static int part_is_free(struct flex_array_part *part)
{
int i;
for (i = 0; i < sizeof(struct flex_array_part); i++)
if (part->elements[i] != FLEX_ARRAY_FREE)
return 0;
return 1;
}
int rpl_flex_array_shrink(struct flex_array *fa)
{
struct flex_array_part *part;
int part_nr;
int ret = 0;
if (!fa->total_nr_elements || !fa->element_size)
return 0;
if (elements_fit_in_base(fa))
return ret;
for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++) {
part = fa->parts[part_nr];
if (!part)
continue;
if (part_is_free(part)) {
fa->parts[part_nr] = NULL;
kfree(part);
ret++;
}
}
return ret;
}
隨著在FlexArray
的各個part存入元素, FlexArray
佔用的空間(自身空間加上額外空間)會越來越大, 這時,可以通過以上函式將FlexArray
收縮.收縮的原理是, 如果一個part中沒有元素了,那麼就可以釋放該part.如何判斷沒有元素了呢, 自然就是所有記憶體都變為 0x6c了.