さてと、お買い物も済ませたし、今日の午後はドライバを 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じゃなきゃいけ ないのであった。直った直った。めでたしめでたし。