さてと、お買い物も済ませたし、今日の午後はドライバを kernel 2.6 に対応 させようかしら。はいはいもうすぐ出来ますからね、良い子にしててね、バブ バブバァ〜。はい、出来たわよ。
あれ、何ザマスこれ、ちょっとおかしいじゃないの。4096 byte まではちゃん と DMA 出来るのにページ境界またいだらハングしちゃったわ。いったいどう いうことなの? 大変、もうそろそろ子供達も学校から帰ってくるのに。どうしましょう、、、
という、家庭の主夫なら誰もが一度は経験する悩みを一発解決。
というところでハマっている人を対象としています。まぁ要するに世界中のほとんどの人ですね。
*1) __get_free_pages() とか pci_alloc_consistent() とかを使おう。 *2) 分からない人は第 15 章 430 ページあたりを読もう。
ここ (ch15 p431 の下の方)に 書いてあるように、__get_free_pages() とかを使って連続した複数ページを 確保すると、最初のページしか reference count がセットされない模様。 kernel 2.4 ではそんなことは無かった。2.6 の仕様らしい。ていうか /usr/src/linux/mm/page_alloc.c の set_page_refs() を見ると確かに
set_page_count(page, 1);とかなってる。変更の理由はパフォーマンス向上のため、と書いてあるけど、 普通の人はこんな仕様で大丈夫なのか?
良く分からんけど、ページを確保した後で確保した全てのページの reference count をセットしたら動いてるみたいでっす。
#define EHIBRAMORDER (3) /* 2^3 pages */ #define EHIBRAMSIZE (PAGE_SIZE * (1 << EHIBRAMORDER)) /* 32kbyte, 8pages */ int pciemem_init(void) { int i, ii; Page pageptr; (前略) /* allocate DMA read buffers */ dmarbuf[i] = pci_alloc_consistent(pciemem[i], EHIBRAMSIZE, &dmarbufpa[i]); if (NULL == dmarbuf[i]) { printk(KERN_ALERT "pciemem: pci_alloc_consistent failed\n"); for (ii = i-1; ii >= 0; ii--) { pci_free_consistent(pciemem[ii], EHIBRAMSIZE, dmarbuf[ii], dmarbufpa[ii]); } return (-ENOMEM); } pageptr = virt_to_page((unsigned long)dmarbuf[i]); for (ii = 0; ii < (1 << EHIBRAMORDER); ii++) { set_page_count(pageptr + ii, 1); } (後略) }ドライバを unload する時に kernel 樣がお怒りのメッセージを沢山吐きます が、普通の人はどうせ shutdown する時まで unload しないからキニシナイ。
void pciemem_exit(void) { (前略) for (i = 0; i < npciemem; i++) { pageptr = virt_to_page((unsigned long)dmarbuf[i]); for (ii = 0; ii < (1 << EHIBRAMORDER); ii++) { set_page_count(pageptr + ii, 0); } pci_free_consistent(pciemem[i], EHIBRAMSIZE, dmarbuf[i], dmarbufpa[i]); } (後略) }
ああそうか、最初のページの reference count だけは pci_alloc/free_consistent() が面倒みてるから、ii = 0ってとこがii = 1じゃなきゃいけ ないのであった。直った直った。めでたしめでたし。