BUG:无法处理内核分页请求

BUG: unable to handle kernel paging request

出于某些特定原因,我需要编辑 2.6.32.65 Linux 内核,以便在内存中分配页面之前从硬盘读取扇区到特定位置。例如,在 mm/filemap.c 中的 do_generic_file_read 函数中,我执行以下操作:

myRet = mapping->a_ops->readpage(filp,myPage);          //Added
Function_Operates_On_MyPage();                          //Added

page = page_cache_alloc_cold(mapping);
if (!page) {
    desc->error = -ENOMEM;
    goto out;
}

然后函数实际上再次读取扇区到分配的页面,如下所示:

    error = mapping->a_ops->readpage(filp, page);

当然这不是最佳的,但我只需要它用于测试目的,所以没关系。现在这个工作正常并且可以完成我想要它做的事情。除了 mm/readahead.c 中的 __do_page_cache_readahead 之外,它在其他多个位置也能正常工作。看起来像下面这样:

for (page_idx = 0; page_idx < nr_to_read; page_idx++) {
    pgoff_t page_offset = offset + page_idx;

    if (page_offset > end_index)
        break;

    rcu_read_lock();
    page = radix_tree_lookup(&mapping->page_tree, page_offset);
    rcu_read_unlock();
    if (page)
        continue;

    myRet = mapping->a_ops->readpage(filp,myPage);          //Added
    Function_Operates_On_Mypage();                          //Added
    page = page_cache_alloc_cold(mapping);
    if (!page)
        break;
    page->index = page_offset;
    list_add(&page->lru, &page_pool);
    if (page_idx == nr_to_read - lookahead_size)
        SetPageReadahead(page);
    ret++;
}

if (ret)
    read_pages(mapping, filp, &page_pool, ret);

它调用 read_pages,它实际上将扇区再次读取到它们分配的页面。据我所知,现在 read_pages 和我一样:

for (page_idx = 0; page_idx < nr_pages; page_idx++) {
    struct page *page = list_to_page(pages);
    list_del(&page->lru);
    if (!add_to_page_cache_lru(page, mapping,
                page->index, GFP_KERNEL)) {
        mapping->a_ops->readpage(filp, page);
    }
    page_cache_release(page);
}
ret = 0;

然而 readpageread_pages 中工作正常,但是当我将它添加到 __do_page_cache_readahead 中时它会导致错误 BUG: unable to handle kernel paging request at ffffea0df0668018。这两行之间的唯一区别是获取数据的页面。在其他情况下对我来说效果很好。为什么会这样?如何解决?

编辑 1

readpage 是指向文件 fs/mpage.c 中的函数 mpage_readpage 的指针,该函数在同一文件中调用 do_mpage_readpage。使用 printks 我发现故障实际上发生在 do_mpage_readpage 的第一行,如下所示:

struct inode *inode = page->mapping->host;

问题是我用来从硬盘读取的页面在启动时被标记为保留(我不希望这个位置被分配给任何进程!)。所以我不确定 page->mapping 会是什么。我猜这是导致错误的原因,但我不知道如何解决!我也不确定它在其他位置是如何工作的,可能是因为 readpage 指向了 mpage_readpage 以外的函数。

根据你的代码,我猜想错误发生在page = page_cache_alloc_cold(mapping);。我用谷歌搜索了一下:page_cache_alloc_cold用于在[=15]中找不到想要的页面时分配新页面=] 如果你频繁调用这个函数,内核会oops,因为提供给内核的内存太小了!我遇到过的问题,我的解决方案是在你的module_init()函数中提前分配页面(Pre-allocation)。这样可以避免频繁调用page_cache_alloc_cold。希望对您有所帮助。

经过一番努力,我发现我的错误非常愚蠢和直截了当。我只是使用了一个无效的页面地址(实际上忘记在某个步骤中在 pfn 和页面之间进行转换)。一旦我解决了它就可以正常工作。