/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, }; #endifpposからページサイズ毎に、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って、我々がお世話になることは絶対無い。って言うデバイスってことでした。