I've had reason to look at this ticket again, and have a potential solution that might allow the "hottest inodes" list maintenance to be efficient without scanning all of the inodes each time (which might be in the 100k-1M range). It is impossible that all of the inodes on a client are "hottest", so doing a full inode scan would mostly be wasted effort, and we really want time and effort focused on those inodes that are actually "hot".
Instead of doing a periodic scan of all inodes to find "hot" ones, I think it would be better for the inodes to "add themselves" to the list when they become hot. This can be done easily/efficiently by keeping a "heat threshold" of the minimum file heat needed to be in the "top heat" listing. As each inode is accumulating its own heat (as normal) it will compare whether its new heat is above "heat threshold". If yes, and it is not already on the list, then it will insert itself into the "top heat" list so that it is a candidate for the next time that the "lfs heat_top" list is generated for userspace.
My thought is that the "top heat" list (and when I write "list" I mean "some data structure that can resolve to a sorted list when it is accessed by userspace") should only be lazily maintained in the kernel, to keep it efficient and low overhead. There are a few possible models for how the list could be maintained:
- list maintenance could be done by a periodic background task that walks the list and decays the inode heat for inactive inodes, and removes them from the list if they are no longer above the "heat threshold" (i.e. the heat of the "Nth" item in the sorted list). This could be done in the background and keeps the list "fresh" so that any access is relatively lightweight. It would not need to add inodes to the list, since they add themselves during IO if their heat is high enough, it would only remove cold inodes and update the "heat threshold".
- list maintenance could be done whenever it is accessed from userspace, which reduces background maintenance overhead if the list is not accessed frequently (or ever). However, this potentially also means that the list can grow large/stale if it isn't accessed frequently, and it will not have a good "heat threshold" to make decisions about which inodes should be added to the list. One (not great) option would be that the "top heat" list isn't maintained at all until it is accessed at least once from userspace? That has the drawback that there won't be any useful information available on first access, but would be OK for tools that are accessing the list frequently and avoid overhead on the nodes that don't use this feature at all.
- list maintenance could be done incrementally by the thread doing the IO whenever a new inode is added to the list. That adds a small amount of work to each thread that is adding a hot inode, but it keeps the data fresh without having a large blob of work to be done at one time.
We don't know the "N" that userspace wants the top list for, but this could either be a parameter from the "lfs heat_top" that informs the kernel of "N" for the future (e.g. average of "N" for each access), or it is a tunable parameter (e.g. 500 by default) that can be changed as needed. We probably also want to have an "lctl get_param" interface to dump the "top heat" list and leave it to userspace to process instead of having to run "lfs heat_top" each time to scrape this information into a monitoring tool.
Depending on the implementation, the thread adding the inode to the "top heat" list could do some some maintenance work each time.
- the "top heat" list shouldn't be strictly limited to "N" entries, as inodes may be moving up and down the list continually. Having 2N entries on the list at one time is fine, as long as list maintenance doesn't get too much overhead. The list just needs to give us a small pool of inodes to check when running "lfs heat_top"
- new inodes could be added to the list directly, either a sorted structure/heap/tree, or just at the end of a "holding list" of candidates for future background task processing (or maybe there is only an unsorted LRU list in the kernel, and it is sorted only upon access?)
- if the list has >> "N" inodes on it, then the adding process would find the oldest (LRU) or coldest (few) inode(s) on the list, decay their heat, and drop any from the list if heat < "heat threshold"
- update the "heat threshold" based on the N'th item on the list (approximately) to give a lower bound for new inodes being added to the list
- rather than scanning for the N'th item on the list (if this is not easily found) there could just be an incremental adjustment like "increase by n% if the list is getting too large" and "decrease by n% if the list is getting too short"
- I don't think there needs to be list maintenance done on every inode heat update (which is a lot of overhead if trying to keep a sorted list, and active inodes will maintain their own heat). This only needs to be done when a new inode is added to the list, or after T seconds since the last list update, so that the list does not grow too large or become stale if the list is never read from userspace
- if this is being done incrementally for each new inode added to the list then there may not be a need to have a dedicated background process
This would likely benefit from the improved LDLM LRU handling (e.g. patch https://review.whamcloud.com/56648 "LU-11509 ldlm: Implement LFRU cache eviction") so that we don't just have strict LRU eviction of inodes from the client cache, otherwise clients may have difficulty to accumulate any history of inode usage. It may be that the file heat can also help inform the DLM LFRU handling to make better choices of what should stay in the client LRU, and which locks should be cancelled.
I've had reason to look at this ticket again, and have a potential solution that might allow the "hottest inodes" list maintenance to be efficient without scanning all of the inodes each time (which might be in the 100k-1M range). It is impossible that all of the inodes on a client are "hottest", so doing a full inode scan would mostly be wasted effort, and we really want time and effort focused on those inodes that are actually "hot".
Instead of doing a periodic scan of all inodes to find "hot" ones, I think it would be better for the inodes to "add themselves" to the list when they become hot. This can be done easily/efficiently by keeping a "heat threshold" of the minimum file heat needed to be in the "top heat" listing. As each inode is accumulating its own heat (as normal) it will compare whether its new heat is above "heat threshold". If yes, and it is not already on the list, then it will insert itself into the "top heat" list so that it is a candidate for the next time that the "lfs heat_top" list is generated for userspace.
My thought is that the "top heat" list (and when I write "list" I mean "some data structure that can resolve to a sorted list when it is accessed by userspace") should only be lazily maintained in the kernel, to keep it efficient and low overhead. There are a few possible models for how the list could be maintained:
We don't know the "N" that userspace wants the top list for, but this could either be a parameter from the "lfs heat_top" that informs the kernel of "N" for the future (e.g. average of "N" for each access), or it is a tunable parameter (e.g. 500 by default) that can be changed as needed. We probably also want to have an "lctl get_param" interface to dump the "top heat" list and leave it to userspace to process instead of having to run "lfs heat_top" each time to scrape this information into a monitoring tool.
Depending on the implementation, the thread adding the inode to the "top heat" list could do some some maintenance work each time.
This would likely benefit from the improved LDLM LRU handling (e.g. patch https://review.whamcloud.com/56648 "LU-11509 ldlm: Implement LFRU cache eviction") so that we don't just have strict LRU eviction of inodes from the client cache, otherwise clients may have difficulty to accumulate any history of inode usage. It may be that the file heat can also help inform the DLM LFRU handling to make better choices of what should stay in the client LRU, and which locks should be cancelled.