--- /dev/null
+From 4964d3076b1f7dffc275700b720b3b474671244a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 23 Aug 2024 16:27:06 +0000
+Subject: mm/memcontrol: respect zswap.writeback setting from parent cg too
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Mike Yuan <me@yhndnzj.com>
+
+[ Upstream commit e399257349098bf7c84343f99efb2bc9c22eb9fd ]
+
+Currently, the behavior of zswap.writeback wrt. the cgroup hierarchy
+seems a bit odd. Unlike zswap.max, it doesn't honor the value from parent
+cgroups. This surfaced when people tried to globally disable zswap
+writeback, i.e. reserve physical swap space only for hibernation [1] -
+disabling zswap.writeback only for the root cgroup results in subcgroups
+with zswap.writeback=1 still performing writeback.
+
+The inconsistency became more noticeable after I introduced the
+MemoryZSwapWriteback= systemd unit setting [2] for controlling the knob.
+The patch assumed that the kernel would enforce the value of parent
+cgroups. It could probably be workarounded from systemd's side, by going
+up the slice unit tree and inheriting the value. Yet I think it's more
+sensible to make it behave consistently with zswap.max and friends.
+
+[1] https://wiki.archlinux.org/title/Power_management/Suspend_and_hibernate#Disable_zswap_writeback_to_use_the_swap_space_only_for_hibernation
+[2] https://github.com/systemd/systemd/pull/31734
+
+Link: https://lkml.kernel.org/r/20240823162506.12117-1-me@yhndnzj.com
+Fixes: 501a06fe8e4c ("zswap: memcontrol: implement zswap writeback disabling")
+Signed-off-by: Mike Yuan <me@yhndnzj.com>
+Reviewed-by: Nhat Pham <nphamcs@gmail.com>
+Acked-by: Yosry Ahmed <yosryahmed@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Michal Koutný <mkoutny@suse.com>
+Cc: Muchun Song <muchun.song@linux.dev>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Shakeel Butt <shakeel.butt@linux.dev>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ Documentation/admin-guide/cgroup-v2.rst | 7 ++++---
+ mm/memcontrol.c | 12 +++++++++---
+ 2 files changed, 13 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
+index b69f701b2485..4a7a59bbf76f 100644
+--- a/Documentation/admin-guide/cgroup-v2.rst
++++ b/Documentation/admin-guide/cgroup-v2.rst
+@@ -1706,9 +1706,10 @@ PAGE_SIZE multiple when read back.
+ entries fault back in or are written out to disk.
+
+ memory.zswap.writeback
+- A read-write single value file. The default value is "1". The
+- initial value of the root cgroup is 1, and when a new cgroup is
+- created, it inherits the current value of its parent.
++ A read-write single value file. The default value is "1".
++ Note that this setting is hierarchical, i.e. the writeback would be
++ implicitly disabled for child cgroups if the upper hierarchy
++ does so.
+
+ When this is set to 0, all swapping attempts to swapping devices
+ are disabled. This included both zswap writebacks, and swapping due
+diff --git a/mm/memcontrol.c b/mm/memcontrol.c
+index ff1e7d2260ab..5c44d3d304da 100644
+--- a/mm/memcontrol.c
++++ b/mm/memcontrol.c
+@@ -5804,8 +5804,7 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
+ WRITE_ONCE(memcg->soft_limit, PAGE_COUNTER_MAX);
+ #if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_ZSWAP)
+ memcg->zswap_max = PAGE_COUNTER_MAX;
+- WRITE_ONCE(memcg->zswap_writeback,
+- !parent || READ_ONCE(parent->zswap_writeback));
++ WRITE_ONCE(memcg->zswap_writeback, true);
+ #endif
+ page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
+ if (parent) {
+@@ -8444,7 +8443,14 @@ void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size)
+ bool mem_cgroup_zswap_writeback_enabled(struct mem_cgroup *memcg)
+ {
+ /* if zswap is disabled, do not block pages going to the swapping device */
+- return !zswap_is_enabled() || !memcg || READ_ONCE(memcg->zswap_writeback);
++ if (!zswap_is_enabled())
++ return true;
++
++ for (; memcg; memcg = parent_mem_cgroup(memcg))
++ if (!READ_ONCE(memcg->zswap_writeback))
++ return false;
++
++ return true;
+ }
+
+ static u64 zswap_current_read(struct cgroup_subsys_state *css,
+--
+2.43.0
+
--- /dev/null
+From b4688af80a8dfb864cde48c5c6aab25145780196 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Jun 2024 02:45:14 +0000
+Subject: mm: zswap: rename is_zswap_enabled() to zswap_is_enabled()
+
+From: Yosry Ahmed <yosryahmed@google.com>
+
+[ Upstream commit 2b33a97c94bc44468fc1d54b745269c0cf0b7bb2 ]
+
+In preparation for introducing a similar function, rename
+is_zswap_enabled() to use zswap_* prefix like other zswap functions.
+
+Link: https://lkml.kernel.org/r/20240611024516.1375191-1-yosryahmed@google.com
+Signed-off-by: Yosry Ahmed <yosryahmed@google.com>
+Reviewed-by: Barry Song <baohua@kernel.org>
+Reviewed-by: Nhat Pham <nphamcs@gmail.com>
+Cc: Chengming Zhou <chengming.zhou@linux.dev>
+Cc: Chris Li <chrisl@kernel.org>
+Cc: David Hildenbrand <david@redhat.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: e39925734909 ("mm/memcontrol: respect zswap.writeback setting from parent cg too")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/linux/zswap.h | 4 ++--
+ mm/memcontrol.c | 2 +-
+ mm/zswap.c | 2 +-
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/include/linux/zswap.h b/include/linux/zswap.h
+index 2a85b941db97..ce5e7bfe8f1e 100644
+--- a/include/linux/zswap.h
++++ b/include/linux/zswap.h
+@@ -35,7 +35,7 @@ void zswap_swapoff(int type);
+ void zswap_memcg_offline_cleanup(struct mem_cgroup *memcg);
+ void zswap_lruvec_state_init(struct lruvec *lruvec);
+ void zswap_folio_swapin(struct folio *folio);
+-bool is_zswap_enabled(void);
++bool zswap_is_enabled(void);
+ #else
+
+ struct zswap_lruvec_state {};
+@@ -60,7 +60,7 @@ static inline void zswap_memcg_offline_cleanup(struct mem_cgroup *memcg) {}
+ static inline void zswap_lruvec_state_init(struct lruvec *lruvec) {}
+ static inline void zswap_folio_swapin(struct folio *folio) {}
+
+-static inline bool is_zswap_enabled(void)
++static inline bool zswap_is_enabled(void)
+ {
+ return false;
+ }
+diff --git a/mm/memcontrol.c b/mm/memcontrol.c
+index 332f190bf3d6..ff1e7d2260ab 100644
+--- a/mm/memcontrol.c
++++ b/mm/memcontrol.c
+@@ -8444,7 +8444,7 @@ void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size)
+ bool mem_cgroup_zswap_writeback_enabled(struct mem_cgroup *memcg)
+ {
+ /* if zswap is disabled, do not block pages going to the swapping device */
+- return !is_zswap_enabled() || !memcg || READ_ONCE(memcg->zswap_writeback);
++ return !zswap_is_enabled() || !memcg || READ_ONCE(memcg->zswap_writeback);
+ }
+
+ static u64 zswap_current_read(struct cgroup_subsys_state *css,
+diff --git a/mm/zswap.c b/mm/zswap.c
+index a50e2986cd2f..ac65758dd2af 100644
+--- a/mm/zswap.c
++++ b/mm/zswap.c
+@@ -131,7 +131,7 @@ static bool zswap_shrinker_enabled = IS_ENABLED(
+ CONFIG_ZSWAP_SHRINKER_DEFAULT_ON);
+ module_param_named(shrinker_enabled, zswap_shrinker_enabled, bool, 0644);
+
+-bool is_zswap_enabled(void)
++bool zswap_is_enabled(void)
+ {
+ return zswap_enabled;
+ }
+--
+2.43.0
+