)]}'
{
  "commit": "9783aa9917f8ae24759e67bf882f1aba32fe4ea1",
  "tree": "2ddbf2490acba84a3f96442b65a830758a079430",
  "parents": [
    "518a86713078168acd67cf50bc0b45d54b4cce6c"
  ],
  "author": {
    "name": "Chris Down",
    "email": "chris@chrisdown.name",
    "time": "Sun Oct 06 17:58:32 2019 -0700"
  },
  "committer": {
    "name": "Linus Torvalds",
    "email": "torvalds@linux-foundation.org",
    "time": "Mon Oct 07 15:47:20 2019 -0700"
  },
  "message": "mm, memcg: proportional memory.{low,min} reclaim\n\ncgroup v2 introduces two memory protection thresholds: memory.low\n(best-effort) and memory.min (hard protection).  While they generally do\nwhat they say on the tin, there is a limitation in their implementation\nthat makes them difficult to use effectively: that cliff behaviour often\nmanifests when they become eligible for reclaim.  This patch implements\nmore intuitive and usable behaviour, where we gradually mount more\nreclaim pressure as cgroups further and further exceed their protection\nthresholds.\n\nThis cliff edge behaviour happens because we only choose whether or not\nto reclaim based on whether the memcg is within its protection limits\n(see the use of mem_cgroup_protected in shrink_node), but we don\u0027t vary\nour reclaim behaviour based on this information.  Imagine the following\ntimeline, with the numbers the lruvec size in this zone:\n\n1. memory.low\u003d1000000, memory.current\u003d999999. 0 pages may be scanned.\n2. memory.low\u003d1000000, memory.current\u003d1000000. 0 pages may be scanned.\n3. memory.low\u003d1000000, memory.current\u003d1000001. 1000001* pages may be\n   scanned. (?!)\n\n* Of course, we won\u0027t usually scan all available pages in the zone even\n  without this patch because of scan control priority, over-reclaim\n  protection, etc.  However, as shown by the tests at the end, these\n  techniques don\u0027t sufficiently throttle such an extreme change in input,\n  so cliff-like behaviour isn\u0027t really averted by their existence alone.\n\nHere\u0027s an example of how this plays out in practice.  At Facebook, we are\ntrying to protect various workloads from \"system\" software, like\nconfiguration management tools, metric collectors, etc (see this[0] case\nstudy).  In order to find a suitable memory.low value, we start by\ndetermining the expected memory range within which the workload will be\ncomfortable operating.  This isn\u0027t an exact science -- memory usage deemed\n\"comfortable\" will vary over time due to user behaviour, differences in\ncomposition of work, etc, etc.  As such we need to ballpark memory.low,\nbut doing this is currently problematic:\n\n1. If we end up setting it too low for the workload, it won\u0027t have\n   *any* effect (see discussion above).  The group will receive the full\n   weight of reclaim and won\u0027t have any priority while competing with the\n   less important system software, as if we had no memory.low configured\n   at all.\n\n2. Because of this behaviour, we end up erring on the side of setting\n   it too high, such that the comfort range is reliably covered.  However,\n   protected memory is completely unavailable to the rest of the system,\n   so we might cause undue memory and IO pressure there when we *know* we\n   have some elasticity in the workload.\n\n3. Even if we get the value totally right, smack in the middle of the\n   comfort zone, we get extreme jumps between no pressure and full\n   pressure that cause unpredictable pressure spikes in the workload due\n   to the current binary reclaim behaviour.\n\nWith this patch, we can set it to our ballpark estimation without too much\nworry.  Any undesirable behaviour, such as too much or too little reclaim\npressure on the workload or system will be proportional to how far our\nestimation is off.  This means we can set memory.low much more\nconservatively and thus waste less resources *without* the risk of the\nworkload falling off a cliff if we overshoot.\n\nAs a more abstract technical description, this unintuitive behaviour\nresults in having to give high-priority workloads a large protection\nbuffer on top of their expected usage to function reliably, as otherwise\nwe have abrupt periods of dramatically increased memory pressure which\nhamper performance.  Having to set these thresholds so high wastes\nresources and generally works against the principle of work conservation.\nIn addition, having proportional memory reclaim behaviour has other\nbenefits.  Most notably, before this patch it\u0027s basically mandatory to set\nmemory.low to a higher than desirable value because otherwise as soon as\nyou exceed memory.low, all protection is lost, and all pages are eligible\nto scan again.  By contrast, having a gradual ramp in reclaim pressure\nmeans that you now still get some protection when thresholds are exceeded,\nwhich means that one can now be more comfortable setting memory.low to\nlower values without worrying that all protection will be lost.  This is\nimportant because workingset size is really hard to know exactly,\nespecially with variable workloads, so at least getting *some* protection\nif your workingset size grows larger than you expect increases user\nconfidence in setting memory.low without a huge buffer on top being\nneeded.\n\nThanks a lot to Johannes Weiner and Tejun Heo for their advice and\nassistance in thinking about how to make this work better.\n\nIn testing these changes, I intended to verify that:\n\n1. Changes in page scanning become gradual and proportional instead of\n   binary.\n\n   To test this, I experimented stepping further and further down\n   memory.low protection on a workload that floats around 19G workingset\n   when under memory.low protection, watching page scan rates for the\n   workload cgroup:\n\n   +------------+-----------------+--------------------+--------------+\n   | memory.low | test (pgscan/s) | control (pgscan/s) | % of control |\n   +------------+-----------------+--------------------+--------------+\n   |        21G |               0 |                  0 | N/A          |\n   |        17G |             867 |               3799 | 23%          |\n   |        12G |            1203 |               3543 | 34%          |\n   |         8G |            2534 |               3979 | 64%          |\n   |         4G |            3980 |               4147 | 96%          |\n   |          0 |            3799 |               3980 | 95%          |\n   +------------+-----------------+--------------------+--------------+\n\n   As you can see, the test kernel (with a kernel containing this\n   patch) ramps up page scanning significantly more gradually than the\n   control kernel (without this patch).\n\n2. More gradual ramp up in reclaim aggression doesn\u0027t result in\n   premature OOMs.\n\n   To test this, I wrote a script that slowly increments the number of\n   pages held by stress(1)\u0027s --vm-keep mode until a production system\n   entered severe overall memory contention.  This script runs in a highly\n   protected slice taking up the majority of available system memory.\n   Watching vmstat revealed that page scanning continued essentially\n   nominally between test and control, without causing forward reclaim\n   progress to become arrested.\n\n[0]: https://facebookmicrosites.github.io/cgroup2/docs/overview.html#case-study-the-fbtax2-project\n\n[akpm@linux-foundation.org: reflow block comments to fit in 80 cols]\n[chris@chrisdown.name: handle cgroup_disable\u003dmemory when getting memcg protection]\n  Link: http://lkml.kernel.org/r/20190201045711.GA18302@chrisdown.name\nLink: http://lkml.kernel.org/r/20190124014455.GA6396@chrisdown.name\nSigned-off-by: Chris Down \u003cchris@chrisdown.name\u003e\nAcked-by: Johannes Weiner \u003channes@cmpxchg.org\u003e\nReviewed-by: Roman Gushchin \u003cguro@fb.com\u003e\nCc: Michal Hocko \u003cmhocko@kernel.org\u003e\nCc: Tejun Heo \u003ctj@kernel.org\u003e\nCc: Dennis Zhou \u003cdennis@kernel.org\u003e\nCc: Tetsuo Handa \u003cpenguin-kernel@i-love.sakura.ne.jp\u003e\nSigned-off-by: Andrew Morton \u003cakpm@linux-foundation.org\u003e\nSigned-off-by: Linus Torvalds \u003ctorvalds@linux-foundation.org\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "0fa8c0e615c2853587e2a87fa396fb5dbac321bb",
      "old_mode": 33188,
      "old_path": "Documentation/admin-guide/cgroup-v2.rst",
      "new_id": "5361ebec33612d62e8078e630d35e517db2a73b0",
      "new_mode": 33188,
      "new_path": "Documentation/admin-guide/cgroup-v2.rst"
    },
    {
      "type": "modify",
      "old_id": "98380779f6d5b57406a1183cecbc983ef6000bd8",
      "old_mode": 33188,
      "old_path": "include/linux/memcontrol.h",
      "new_id": "fa9ba2edf7e089c7750a8d0c6b476efbcef0da0e",
      "new_mode": 33188,
      "new_path": "include/linux/memcontrol.h"
    },
    {
      "type": "modify",
      "old_id": "c313c49074cad4916e7aa6b74968c548690a451d",
      "old_mode": 33188,
      "old_path": "mm/memcontrol.c",
      "new_id": "bdac56009a38c0c84cd36bbf1bd474537af76a17",
      "new_mode": 33188,
      "new_path": "mm/memcontrol.c"
    },
    {
      "type": "modify",
      "old_id": "e5d52d6a24aff1c7fccd292bb8a2455e1eec1d52",
      "old_mode": 33188,
      "old_path": "mm/vmscan.c",
      "new_id": "dfefa1d99d1b085f331b4529dc5e1507971eaf74",
      "new_mode": 33188,
      "new_path": "mm/vmscan.c"
    }
  ]
}
