在 Linux kernel 當中有許多的 common struct,例如說 ieee80211_txq
這樣的結構,透過這樣的方式,可以讓不同的 driver 重用相同的 struct。但是,針對每個不同的 driver,還是會有不同的 private data,這時候就會用 private data struct 的方式來處理。
例如說,struct ieee80211_txq
是 mac80211 中的 intermediate tx queue,目前有使用的 wireless driver 為 ath9k, ath10k 以及 mt76。struct ieee80211_txq
的整個結構為:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/** * struct ieee80211_txq – Software intermediate tx queue * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * @sta: station table entry, %NULL for per-vif queue * @tid: the TID for this queue (unused for per-vif queue) * @ac: the AC for this queue * @drv_priv: driver private area, sized by hw->txq_data_size * * The driver can obtain packets from this queue by calling * ieee80211_tx_dequeue(). */ struct ieee80211_txq { struct ieee80211_vif *vif; struct ieee80211_sta *sta; u8 tid; u8 ac; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; |
可以看到,除了前四個 member 外,最後還定義了一個 drv_priv
,這是用來指向各個 driver 的 private data 的 member。以 ath9k 為例,這邊是指向一個 struct ath_atx_tid
,ath10k 則是 struct ath10k_txq
。
以 ath9k 為例,這邊是在 sta_info_alloc
的地方 allocated:
1 2 3 4 5 6 7 |
int size = sizeof(struct txq_info) + ALIGN(hw->txq_data_size, sizeof(void *)); txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { struct txq_info *txq = txq_data + i * size; ieee80211_txq_init(sdata, sta, txq, i); } |
先看 struct txq_info
的部份:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/** * struct txq_info – per tid queue * * @tin: contains packets split into multiple flows * @def_flow: used as a fallback flow when a packet destined to @tin hashes to * a fq_flow which is already owned by a different tin * @def_cvars: codel vars for @def_flow * @frags: used to keep fragments created after dequeue */ struct txq_info { struct fq_tin tin; struct fq_flow def_flow; struct codel_vars def_cvars; struct codel_stats cstats; struct sk_buff_head frags; unsigned long flags; /* keep last! */ struct ieee80211_txq txq; }; |
重點在於 int size =
這行,除了 allocate struct txq_info
的 size 之外,還加上了 private data 的 size (hw->txq_data_size
),透過這樣的方式,讓每個 driver 可以使用自身會使用的 data。
範例程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#include <stdio.h> #include <stdlib.h> #define ALIGN(x, a) (((x) + (a) – 1) & ~((a) – 1)) struct foobar { int a; int b; int c[10000]; int d; }; struct foo { int a; int b; char priv[0]; }; struct bar { int a; int b; struct foo this;。 }; struct barbar { struct bar foo; }; struct zero { int a; int b; char c[100]; }; struct one { struct zero z; }; int main() { int size = sizeof(struct bar) + ALIGN(sizeof(struct foobar), sizeof(void *)); printf(“%d\n”, sizeof(struct zero)); printf(“%d\n”, sizeof(struct one)); printf(“%d\n”, sizeof(struct barbar)); struct bar *p = malloc(size); struct bar *v = malloc(size); p->a = 10; p->b = 20; p->this.a = 30; struct foobar *fb = (struct foobar *) p->this.priv; fb->a = 50; fb->b = 100; fb->d = 200; printf(“size: %d\n”, size); printf(“%d %d %d\n”, p->a, p->b, p->this.a); printf(“%d %d %d\n”, fb->a, fb->b, fb->d); } |
可以嘗試使用 gdb 觀察不加上 struct foobar size 的狀況下,會有什麼問題。
Leave a Reply