【翻译】 DMA和get_user_pages()

LWN.net需要你!

没有订阅者,LWN将根本不存在。 请考虑注册订阅,帮助LWN继续出版。

作者: Jake Edge
2018年12月12日
Linux管道工会议

在2018年Linux Plumbers大会(LPC)的RDMA微型会议上,John Hubbard、Dan Williams和Matthew Wilcox主持了关于围绕get_user_pages()(和朋友)的问题以及与DMA的互动的讨论。 这并不是第一次出现这个话题,早在4月份的Linux存储、文件系统和内存管理峰会上也有过相关讨论。 简而言之,问题在于内核的多个部分认为它们对同一块内存负有责任,但它们没有协调它们的活动;正如人们所猜测的那样,有时会出现混乱。

Hubbard首先阐述了本次会议的目标。 这个想法是为了确保每个人都知道这个问题;尽管它已经在各种邮件列表线程和其他方面进行了讨论,但每个人都可能没有达到这个速度。 他希望能就长期的解决方案达成共识;他已经发布了一些RFC,但解决方案却有点争议。

他说,使用一种常见的代码模式使内核崩溃并不难。 他说,一个应用程序在内存中插入页面,导致内核使用get_user_pages()或类似的方法将用户空间的内存映射到内核的地址空间。 然后它为这些页面设置DMA。 Hubbard说,虽然这是RDMA微会议,但问题存在于所有的DMA操作(例如,GPU或FPGA),而不仅仅是通过网络远程操作。 然后,驱动程序将目标页标记为脏,并用put_page()(或release_pages())释放它们。 但如果这些页是由一个非基于RAM的文件系统支持的,事情会出错。 这种模式从2005年左右开始就被认为是合理的,但它实际上是坏的。

[John Hubbard & Dan Williams]

根本的问题是,当页面被钉住并进行I/O时,页面缓冲区可以被剥离;回写代码认为它已经写入了脏数据,因此不需要缓冲区,但是当I/O完成时,驱动程序会再次标记页面为脏。 一旦发生这种情况,回写可能会(再次)出现,期望页面缓冲区仍然存在。 从本质上讲,文件系统不知道一个页面可以在它的权限之外被标记为脏。 有两个子系统不同意哪个是脏状态的维护者。 他说,所有这些都意味着,今天的Linux并不真正支持DMA到以文件为基础的内存。

Hubbard对这次崩溃做了更详细的介绍,可以在他的幻灯片中看到[PDF],其中还提到了Jan Kara关于这个问题的一封长长的电子邮件。 Kara被列为会议牵头人之一,但他无法参加LPC。

在回答一位与会者的问题时,Hubbard说,缓冲区被释放是因为reclaim认为它将会比实际情况更进一步。 它无法取得进展,因为get_user_pages()已经取得了对这些页面的引用。 Reclaim已经释放了缓冲区,但后来又不得不退了出来。 Williams补充说,如果reclaim不插手,一切都能正常工作;当reclaim在驱动程序之前到达页面时,问题就发生了。

Mel Gorman说,文件系统从根本上假定page_mkwrite()会在页面被标记为脏之前被调用。 所以当reclaim出现时,页面可能是脏的,但文件系统认为摆脱缓冲区是安全的。 他指出,dirty并不是一个真正的二进制状态。

有人问这是个多大的问题,它是每天、每周还是每月发生的? Hubbard说,对他来说,这是可靠的可重复的;他已经建议客户避免对文件支持的内存做DMA。 但是,人们写了这样的代码,测试成功后,在更紧张的情况下部署时却失败了,这已经很罕见了,一位与会者说。 另一位与会者提出,一个错误越是不容易重现,修复它就越是重要。 Hubbard表示同意,他说他记得有一个bug花了一个月的时间来重现,这意味着最后他花了一年的时间来修复它。 Wilcox补充说,这肯定需要修复,因为越来越多的系统碰到了这个问题。

提案

Hubbard建议get_user_pages()在页面结构中 "做一个注释",有效地说明"get_user_pages()曾经在这里"。 一个问题是,结构页已经满了--这是一个长期存在的问题。 Wilcox提出的一个想法是,在结构页中使用LRU的下一个和上一个指针作为标志,并使用一个引用计数来跟踪这个问题;Hubbard说,这些页面在被钉住时将从LRU列表中删除。

Hubbard提议的方法是用新的put_user_page*()调用来取代所有受影响的put_page()release_pages()调用,在处理了跟踪信息并将页面恢复到LRU列表后,再调用put_page()。 他的RFC补丁集转换了InfiniBand驱动,但还有大约100个其他地方需要转换。 他说,其中有些地方需要进行相当多的分析,以确定如何转换它们。

Williams提醒说,他正在DAX中使用LRU指针,Jérôme Glisse也在他的异构内存管理(HMM)补丁中使用它们。 Wilcox告诫说,他在结构页中为DAX和HMM腾出了 "很多空间";"你们两个人都会有空间的"。 解决这个问题很重要,Hubbard说,因为没有什么能阻止用户这样做,就像他们自2005年以来所做的那样;"这只是没有用",Williams补充说。

在第一个RFC发布后,Andrew Morton问Hubbard如何确保所有的转换已经完成。 不仅需要解决这个问题,而且要显示它已经解决。 为此,他想在代码中加入一个断言,如果put_user_pages()在应该被调用时没有被调用,就会触发。 不过,他需要在结构页中设置一个位来跟踪。 Wilcox说,他还有一个位可以让Hubbard使用,特别是如果它只是在转换过程中临时使用的话;"我一直在为你保存它,John",Wilcox笑着说。 威廉姆斯警告说,"第一个比特是免费的"--引起了广泛的笑声。

哈伯德说,他在会议中试图完成的部分工作是提高人们对这套补丁即将到来的认识。 RFC只是六个补丁,其中包括InfiniBand的转换,这已经被Infiniband开发者审查过了。 但他想花点时间深入每个子系统,确保了解它是如何使用内存的。 目前,有一个假的put_user_page(),它只是做了一个put_page()

到目前为止,他所描述的所有工作都只是追踪那些已经用get_user_page()钉住的页面。 一旦你有了这些信息,你就需要对它做一些事情。 停止缓冲区的移除是必要的,可以通过暂停try_to_unmap()来完成。 但是文件系统的开发者说,在这种状态下保留页面并不是一个好主意,所以Kara和其他人说,内核仍然可以允许回写,但是用反弹缓冲区来做,这样就不会受到对这些页面的其他操作的影响。

撤销()

Williams说,他想谈谈revoke()的API,这将有助于解决mmap()区域被共享并被用于DMA的这些问题。 如果另一个进程想在进行DMA的区域内截断文件或在文件上打洞,"你就完蛋了",至少对DAX是这样。

但是,正如他过去所做的那样,Glisse指出,有些东西是不能被撤销的。 它只能在支持它的设备上工作。 有些驱动程序可以停止DMA,但有些驱动程序只是钉住内存,没有办法停止DMA和解除钉住内存。 Wilcox说,这取决于硬件,即使是NVMe,它有一个取消未完成I/O的命令,大多数设备只是没有实现它。 他说,这些内存不能被重新使用,直到硬件说它不会再在那里进行DMA。 威廉姆斯建议,revoke()调用应该只是等待设备,但对于一些设备来说,这种等待可能是永远的,一位与会者说。

所有调用get_user_pages()的东西都需要被审计,但Williams担心一些驱动程序并不真正知道他们的页面是否来自get_user_pages()。 Hubbard说,他不得不在一些开发中的转换中传递一些额外的跟踪信息。 Wilcox想知道,如果他能在页面结构中永久地释放一点,以追踪这些是否是用get_user_pages()钉住的页面,那是否就不需要改变所有的调用站点,因为put_page()可以简单地做正确的事情?

有一些人认为这可能是可行的,但它没有持续很久。 没有办法区分put_page()的其他用途和应该做的 "正确的事情"。 会有一个引用计数,但是没有办法知道任何给定的put_page()是来自驱动的那个应该清除的位。 所以驱动中的调用站点仍然需要改变。 然而,这将有助于跟踪这些类型的页面,从而使额外的信息不必从上层传递下来。

一位与会者对伴随着RFC补丁的性能数字提出了质疑。 虽然他们显示这些变化对性能影响不大,但这些数字比这些设备应该能够做到的要低很多。 令人担忧的是,这些数字是如此之低,以至于它们甚至不应该被比较,以确定这些变化实际上产生了什么影响(如果有的话)。 从LPC之后的一个主题中可以看出,有一个测量问题;一旦它被解决,影响仍然是最小的。

回到revoke()的话题,Williams说他的目标是摆脱短期和长期DMA之间的区别。 添加这种区别是为了让DAX可以简单地拒绝任何将其内存用于长期DMA的尝试。 不仅仅是RDMA会这样做,还有其他设备,比如视频卸载设备,也会永远占用内存。 他建议,也许文件租约可以提供一个处理问题的模型。如果另一个进程做了一个文件截断操作,租约机制可以提供一种方法,把内存从设备上拉走。

Jason Gunthorpe建议,当内存被用于DMA时,截断操作就会失败。 Wilcox说,他多年来一直主张这样做,但其他内核开发者不会允许这样做。

Boaz Harrosh说,这些页面属于文件系统,所以错误在于让内核的其他部分以文件系统看不到的方式处理它们。 他建议DMA用户为文件中用于DMA的部分设置一个范围锁,但是Williams说他们不能 "重写宇宙",说每个人都必须设置一个范围锁。 Wilcox说,这也会对性能产生影响,这是不可接受的。

很明显,没有简单的解决方案,但计划的路径似乎是大多数人都同意的。 Wilcox指出,通过在get_user_pages()中从LRU列表中删除这些页面,回写将永远不会找到它们,所以崩溃不会发生。 对于DAX来说,事情并不那么美好,但RDMA的开发者似乎愿意尝试处理被告知他们为DMA钉住的内存要消失的情况,至少在特殊情况下是这样。 当然,这似乎是一个会再次出现的话题--在未来几年可能会多次出现。

该会议的YouTube视频可供观看。

[我要感谢LWN的旅行赞助商,Linux基金会,为前往温哥华参加LPC提供了帮助。

这篇文章的索引条目
内核 内存管理/get_user_pages()
会议 Linux Plumbers Conference/2018


(登录后可发表评论)

猜你喜欢

转载自blog.csdn.net/community_717/article/details/130868257