適用範囲:DS-5
解決策
メモリマップされたパフォーマンスカウンタをStreamlineで定義する際、モニタされるデバイスは通常ドライバとして制御されることが多く、また、ランタイムにモジュールとしてロードされるか、カーネルにスタティックにリンクされているのが難しい点となります。ほとんどのケースにおいて、通常ioremap()関数を用いてドライバによってリマップされる制御レジスタの特殊ケースとしてカウンタが存在します。
レジスタの同一セットを1つ以上のマッピングに生成することは可能ではありますが、これは良い手法ではなく、代替えの方法があるのであればこの手法をとるべきではありません。
メモリマップされたパフォーマンスカウンタを生成して割り付けるには主に3つの方法があります:
-ダイレクトアクセス、二重マッピング
最良の方法ではありませんが、二重マッピングが必要な場合があります。たとえば、メインのドライバ、PL310 L2キャッシュコントロールドライバ(Linuxカーネルツリー内arch/arm/mm/cache-l2x0.c)がデバイスの初期化及び動作制御を行うためにレジスタにアクセスする場合です。同時に一部のレジスタがパフォーマンスデータを読み込むために使われますが、ドライバがこの機能を実装していません。
ドライバはカーネルの主要部分であり、これを制御することはできません。この記事で言及されているリザベーションを無効にし、2つ目の制御レジスタのための固有マッピングを作成することが必要です。これはgator_events_pl310.cファイル内にあるgator_events_pl310_probe()関数においてgatorのイベントソースの実装で行うことができます。ここではioremap()の呼び出しによってはpl310_base仮想ベースアドレスを生成します。続いて、このアドレスをPL310のレジスタのアクセスに使用します。例えば、readl()を用いてgator_events_pl310_read()関数のパフォーマンスデータを読み出します。
-ダイレクトアクセス、シングルマッピング
PL310ドライバについて制御可能であれば、l2x0_get_base()といった関数を用いて仮想ベースアドレスをマッピングできるように内部APIを追加することができます。
Gatorイベントソースのインプリメンテーションはioremap()のかわりにそれを呼び出し、readl()関数を用いてパフォーマンスカウンタにアクセスする為にその戻り値を使用します。
これは、二重マッピング方式による明確ではないアプローチを不要としますが、まだ欠点が含まれています。一番クリティカルなものは、デバイスレジスタが2つの別個のコードセットからアクセスされるものです。最悪のシナリオでは、gatorインプリメンテーションのバグによって追跡が難しい深刻な問題であるL2キャッシュ設定の破損を引き起こす可能性がある事です。
-Performance API
PL310のドライバがパフォーマンスデータにアクセスするためのAPIを提供する場合、これをgatorモジュール内から直接呼び出すことができます。たとえば、EXPORT_SYMBOLマクロを用いてl2x0_get_perf_counter()関数をエクスポートすると、それをgator_events_pl310_read()から呼び出すだけでかまいません。ioremap()やベースアドレスの個別のコピーを用意する必要はありません。もちろん、APIはパフォーマンスモニタ設定の方法を提供する必要があります。
以下がgator_*_readルーチン内のAPIコールのサンプルです:
/* Defined in driver's header file */
extern int l2x0_get_perf_counter(void);
static int gator_myevents_read(int **buffer)
{
int len = 0;
if (myevent_enabled) {
myevent_buffer[len++] = myevent_key;
myevent_buffer[len++] = l2x0_get_perf_counter();
}
return len;
}
このアプローチではデバイスを排他リソースとします。メインドライバだけがそれらのレジスタにアクセス可能です。このアプローチでは1つだけ副作用があります。デバイスドライバはgatorモジュールがロードされたとき、カーネル内に存在しなければなりません。モジュールローダはl2x0_get_perf_counter()関数へのあらゆる参照を解決しなければならないからです。これは静的にリンクされたドライバでは問題となりませんが、デバイスドライバがローダブルモジュールである場合には特定のロード順を要求されることになります。 これらはgatorの前にロードしなければなりません。そうでなければ、カーネルは関連するシンボルの解決ができず、gatorを受け付けることができなくなります。
必要であれば、ダイナミックシンボル解決によってこれを解決することができます。メインドライバの関数にアクセスするためsymbol_get()カーネル関数を使う事ができます。
例:
/* Defined in driver's header file */
extern int l2x0_get_perf_counter(void);
static int (*gator_myevent_get_counter)(void);
static int gator_myevents_start(void)
{
gator_myevent_get_counter = symbol_get~(l2x0_get_perf_counter);
}
static int gator_myevents_stop(void)
{
if (gator_myevent_get_counter)
symbol_put(l2x0_get_perf_counter);
}
static int gator_myevents_read(int **buffer)
{
int len = 0;
if (gator_myevent_get_counter && myevent_enabled) {
myevent_buffer[len++] = myevent_key;
myevent_buffer[len++] = gator_myevent_get_counter();
}
return len;
}
このような実装はl2x0_get_perf_counter()を直接参照しません。そのため、要求されたシンボルが有効でなくてもロードされるでしょう。そのため、キャプチャ操作が開始するとsymbol_get()は関数ポインタを返すか、関数がまだ有効でなければNULLとなります。シンボルの解決は高負荷な処理であり、gator_*_read()を含む重たいユーザコードでは行われるべきではありません。