c - 如何取消映射并释放linux模块中的highmem页面

标签 c linux linux-kernel

我在模块的 init 方法中分配并映射了 highmem 页面:

highmem_page = alloc_pages( GFP_HIGHUSER, 3 );
if ( ! highmem_page ) printk(KERN_ERR "Couldn't allocate highmem page with order : 3\n" );
  else {
    highmem_page_address = kmap( highmem_page );
    if ( ! highmem_page_address ) printk( KERN_ERR "Couldn't map highmem pages.\n");
    else {
      printk( KERN_ERR "Address for highuser pages is : %lx. Order: %d.\n", 
            highmem_page_address,
            3
           );
     }
  }

效果很好,并在日志中生成了输出。通过dmesg:

[ 4065.975025] Address for highuser pages is : ffff88002c5d0000. Order: 3.

现在,在退出时,我正在做这样的事情:

kunmap(highmem_page); //will unmap only ? can it free too? highly doubtful!
__free_pages( highmem_page, 3 ); //will free the pages?

我认为kunmap()应该从内核的虚拟地址空间取消映射highmem页面。 我的理解对吗?然后__free_pages()应该释放分配的页面。

但是,当我 rmmod 它时,dmesg 显示了一些我没有预料到的内容:

[ 4109.529834] BUG: unable to handle kernel paging request at ffffeb880002c5dc
[ 4109.529845] IP: [<ffffffff8111e999>] __free_pages+0x9/0x40
[ 4109.529856] PGD 0 
[ 4109.529861] Oops: 0000 [#1] SMP 
[ 4109.529866] CPU 0 
[ 4109.529869] Modules linked in: prob4(O-) 
...
[ 4109.529943] 
[ 4109.529948] Pid: 5060, comm: rmmod Tainted: G           O 3.2.6 #4 TOSHIBA T20             /T20             
[ 4109.529956] RIP: 0010:[<ffffffff8111e999>]  [<ffffffff8111e999>] __free_pages+0x9/0x40
[ 4109.529963] RSP: 0018:ffff88002c5b5e78  EFLAGS: 00010216
[ 4109.529967] RAX: 000001880002c5c0 RBX: ffffea0000b17400 RCX: 0000000000000024
[ 4109.529970] RDX: 0000000000000000 RSI: 0000000000000006 RDI: ffffeb880002c5c0
[ 4109.529974] RBP: ffff88002c5b5e78 R08: 0000000000000000 R09: 0000000000000005
[ 4109.529978] R10: ffffea0000b18018 R11: 0000000000000000 R12: ffffffffa024f2f8
[ 4109.529981] R13: ffff88002c5b5f18 R14: 00007fffe3342480 R15: 0000000000000001
[ 4109.529986] FS:  00007f927f6d4700(0000) GS:ffff88005f400000(0000) knlGS:0000000000000000
[ 4109.529990] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[ 4109.529993] CR2: ffffeb880002c5dc CR3: 000000002c537000 CR4: 00000000000006f0
[ 4109.529997] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 4109.530000] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 4109.530005] Process rmmod (pid: 5060, threadinfo ffff88002c5b4000, task ffff88005b0416e0)
[ 4109.530008] Stack:
[ 4109.530010]  ffff88002c5b5ea8 ffffffff8111ea14 ffff88002c5b5e98 ffffffff00000006
[ 4109.530017]  ffff88002c5b5ec8 0000000000000007 ffff88002c5b5ec8 ffffffffa024d049
[ 4109.530024]  0000000000000880 ffffffffa024f000 ffff88002c5b5f78 ffffffff810a4cde
[ 4109.530031] Call Trace:
[ 4109.530037]  [<ffffffff8111ea14>] free_pages+0x44/0x50
[ 4109.530043]  [<ffffffffa024d049>] my_pager_exit+0x49/0x1000 [prob4]
[ 4109.530051]  [<ffffffff810a4cde>] sys_delete_module+0x19e/0x270
[ 4109.530059]  [<ffffffff81645102>] system_call_fastpath+0x16/0x1b
[ 4109.530062] Code: 04 25 d8 05 01 00 8b 4d b8 48 8b 55 c0 e9 fe fe ff ff 31 d2 48 89 de e8 06 c6 ff ff e9 6d ff ff ff 90 55 48 89 e5 66 66 66 66 90 <8b> 47 1c f0 ff 4f 1c 0f 94 c0 84 c0 74 09 85 f6 74 0d e8 60 d8 
[ 4109.530120] RIP  [<ffffffff8111e999>] __free_pages+0x9/0x40
[ 4109.530125]  RSP <ffff88002c5b5e78>
[ 4109.530127] CR2: ffffeb880002c5dc
[ 4109.530132] ---[ end trace 2dcb27dca5b2d882 ]---

ffffeb880002c5dc 不在我的代码中。为什么会去那里?

现在我该怎么办?

R# rmmod prob4
ERROR: Removing 'prob4': Device or resource busy

由于未进行卸载。

那么,如何从 HIGHMEM 取消映射并释放分配的页面?

编辑:

顺便说一句,同样的问题在https://unix.stackexchange.com/questions/98498/how-to-unmap-and-free-the-highmem-pages没有产生任何结果这就是我在这里问的原因。而且,这个问题包含与编程相关的实际错误。仅此而已。

最佳答案

您分配多个连续的物理页,然后仅映射第一个。 如果您的内核模块从其他两个模块访问内存,可能会发生奇怪的事情。

通常在整个内核中完成此操作的方式是,如果您需要多个页面,则会有一个循环,您可以在其中:

1. call alloc_page to get a physical page.
2. map it with kmap.

发布时需要循环遍历所有页面并且:

1. call kunmap.
2. call free_page.

您也可以考虑老式的 kmalloc,如果您只需要少量内存,它可以省去一些麻烦。

关于c - 如何取消映射并释放linux模块中的highmem页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19740553/

相关文章:

c - 在 Linux 内核中设置或清除位的平台独立方式

linux - 在这种情况下如何从 Linux 内核模块导出符号?

c - scanf 调用中类型的无效操作数

linux - crontab 无法正常工作

linux - 防止 Docker Compose 创建单独的网络

operating-system - Linux 中的分段 : Segmentation & Paging are redundant?

c - 使用最高有效位来标记 union 是否被认为是一种不好的做法?

c - 如何从终端输入(linux)运行程序?

c - C 中 char[] 和 char* 的区别

linux - 如何为 ubuntu 编写安装脚本