put_page 和 pci_unmap_sg 之间的竞争条件

racecondition between put_page and pci_unmap_sg

我正在研究包含 dma 的设备驱动程序。如果我只使用 get_user_pages_fastput_page 一切正常。一旦我添加了对 pci_(un)map_sg 的调用,它似乎就变得活泼了。

测试程序完成后,我 有时 得到一个 BUG,但我并不总是得到 BUG.

BUG 内容如下:

[ 7202.183324] BUG: Bad page state in process dma  pfn:f20ea8
[ 7202.183327] page:ffffea003c83aa00 count:0 mapcount:-30705 mapping:          (null) index:0xc0054000d3431e41
[ 7202.183329] page flags: 0x2ffff000008000c(referenced|uptodate|swapbacked)

并有以下踪迹:

[ 7202.183442] Call Trace:
[ 7202.183447]  [<ffffffff8171e0be>] dump_stack+0x45/0x56
[ 7202.183453]  [<ffffffff8171aa88>] bad_page.part.61+0xcf/0xe8
[ 7202.183459]  [<ffffffff81155ce8>] free_pages_prepare+0x148/0x160
[ 7202.183463]  [<ffffffff81156651>] free_hot_cold_page+0x31/0x150
[ 7202.183467]  [<ffffffff811567b6>] free_hot_cold_page_list+0x46/0xa0
[ 7202.183473]  [<ffffffff8115c210>] release_pages+0x80/0x210
[ 7202.183478]  [<ffffffff8118b725>] free_pages_and_swap_cache+0x95/0xb0
[ 7202.183483]  [<ffffffff81174c5c>] tlb_flush_mmu.part.56+0x4c/0x90
[ 7202.183486]  [<ffffffff811757d5>] tlb_finish_mmu+0x55/0x60
[ 7202.183491]  [<ffffffff81180c0b>] exit_mmap+0xcb/0x170
[ 7202.183497]  [<ffffffff810646cc>] mmput+0x5c/0x120
[ 7202.183502]  [<ffffffff81069b1c>] do_exit+0x26c/0xa50
[ 7202.183506]  [<ffffffff811bef1e>] ? ____fput+0xe/0x10
[ 7202.183511]  [<ffffffff8106a37f>] do_group_exit+0x3f/0xa0
[ 7202.183515]  [<ffffffff8106a3f4>] SyS_exit_group+0x14/0x20
[ 7202.183520]  [<ffffffff8172ec2d>] system_call_fastpath+0x1a/0x1f

是否有任何我必须为任何功能持有的额外锁(没有针对 pci-dma api 的提示,我在其他驱动程序中看到 put_page 的地方有附近没有锁)。还有一点我可能会出错吗?

我目前将相同的指针传递给 pci_unmap_sg 作为我用于 pci_map_sg 的指针,这似乎没问题,在查看其他驱动程序并使用实际页面计数而不是从返回的计数取消映射的映射。

我想我缺少一些锁定或同步,正如 this post 可能暗示的那样,但是我对 linux 内存子系统的理解很难理解。

另外 [release_pages](http://lxr.free-electrons.com/source/mm/swap.c#L899) 似乎是一个替代方案,但是我在视图路径中看到 spin_unlock_irqrestore(&zone->lru_lock, flags); 没有事先锁定,我需要在调用它之前保持锁定吗?

我错误地初始化了 scatter/gather 列表,我认为调用 sg_set_page 就足够了,但是需要初始调用 sg_init_table

因为我发现很多驱动程序没有这样做,而且 ldd3 也没有在任何地方提到 sg_init_table,所以我想如果我把它留在这里可能会有帮助。