/dev/oldmem
/dev/oldmemって言うデバイス。カーネルオプションCONFIG_CRASH_DUMPで作成され、カーネルクラッシュ時のダンプを取得するためのものらしいですが、よく分かりませんね。クラッシュしたカーネル下でダンプするのは、カーネルが不安定になっていて、よろしくない。と言うことなんでしょう。
そこで登場するのが、kexec。これはカーネル下に、前もって別のカーネルイメージを置いといて、カーネルがクラッシュした場合、このカーネルと差し替える。と言うものだそうです。そして、差し替えたカーネル下で、クラッシュしたカーネルダンプを取得する。と言う按配だそうで・・・。まあ、その時、クラッシュカーネルのダンプは、できるだけカーネルの負荷がないように(この時点では、クラッシュしたカーネルで動作する必要あり。たぶん)、で、単にどっかの物理メモリに退避させるだけ。
カーネルを差し替えるって、プロセス切り替えみたいな。かんじでなく、カーネルは物理ストレートマップしていて、従って、クラッシュしたカーネルの物理メモリーに、新しいカーネルを転送する(上書き)必要があります。その時、クラッシュしたカーネルのイメージを、カーネル空間外の連続するページに蓄えておいて、そのページを新しいカーネル下で、安心して取得しようと言う物らしいです。
とりあえず、以下がソース
なお、引数のchar *bufは、ユーザ空間/カーネル空間のケースでの呼び出しがあるらしく、userbuf引数が1の時、bufはユーザ空間と言うことらしく、その場合、kdump_buf_pageにcopy_page()で1ページ分複写して、それをcopy_to_user()でユーザ空間buffに複写していますが、なぜvaddrでcopy_to_user()しないんでしょうかね・・・。
そこで登場するのが、kexec。これはカーネル下に、前もって別のカーネルイメージを置いといて、カーネルがクラッシュした場合、このカーネルと差し替える。と言うものだそうです。そして、差し替えたカーネル下で、クラッシュしたカーネルダンプを取得する。と言う按配だそうで・・・。まあ、その時、クラッシュカーネルのダンプは、できるだけカーネルの負荷がないように(この時点では、クラッシュしたカーネルで動作する必要あり。たぶん)、で、単にどっかの物理メモリに退避させるだけ。
カーネルを差し替えるって、プロセス切り替えみたいな。かんじでなく、カーネルは物理ストレートマップしていて、従って、クラッシュしたカーネルの物理メモリーに、新しいカーネルを転送する(上書き)必要があります。その時、クラッシュしたカーネルのイメージを、カーネル空間外の連続するページに蓄えておいて、そのページを新しいカーネル下で、安心して取得しようと言う物らしいです。
とりあえず、以下がソース
#ifdef CONFIG_CRASH_DUMP
static const struct file_operations oldmem_fops = {
.read = read_oldmem,
.open = open_oldmem,
.llseek = default_llseek,
};
#endif
pposからページサイズ毎に、copy_oldmem_page()で取得しているようで。pposには、新しいカーネルが機動する時に、クラッシュしたカーネルをダンプしている、物理アドレスが設定されているんじゃないでしょうか?
static ssize_t read_oldmem(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long pfn, offset;
size_t read = 0, csize;
int rc = 0;
while (count) {
pfn = *ppos / PAGE_SIZE;
if (pfn > saved_max_pfn)
return read;
offset = (unsigned long)(*ppos % PAGE_SIZE);
if (count > PAGE_SIZE - offset)
csize = PAGE_SIZE - offset;
else
csize = count;
rc = copy_oldmem_page(pfn, buf, csize, offset, 1);
if (rc < 0)
return rc;
buf += csize;
*ppos += csize;
read += csize;
count -= csize;
}
return read;
}
copy_oldmem_page()で、クラッシュしたカーネルの物理ページ(ページフレーム)で、ページ単位に取得しています。(いま一つ分からん。) まあ、/dev/memと異なって、ページフレーム毎に仮想アドレスをアサインしながら、buffに複写する必要があります。なお、引数のchar *bufは、ユーザ空間/カーネル空間のケースでの呼び出しがあるらしく、userbuf引数が1の時、bufはユーザ空間と言うことらしく、その場合、kdump_buf_pageにcopy_page()で1ページ分複写して、それをcopy_to_user()でユーザ空間buffに複写していますが、なぜvaddrでcopy_to_user()しないんでしょうかね・・・。
ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
size_t csize, unsigned long offset, int userbuf)
{
void *vaddr;
if (!csize)
return 0;
if (!is_crashed_pfn_valid(pfn))
return -EFAULT;
vaddr = kmap_atomic_pfn(pfn);
if (!userbuf) {
memcpy(buf, (vaddr + offset), csize);
kunmap_atomic(vaddr, KM_PTE0);
} else {
if (!kdump_buf_page) {
printk(KERN_WARNING "Kdump: Kdump buffer page not"
" allocated\n");
kunmap_atomic(vaddr, KM_PTE0);
return -EFAULT;
}
copy_page(kdump_buf_page, vaddr);
kunmap_atomic(vaddr, KM_PTE0);
if (copy_to_user(buf, (kdump_buf_page + offset), csize))
return -EFAULT;
}
return csize;
}
/dev/oldmemって、我々がお世話になることは絶対無い。って言うデバイスってことでした。





