On Wed, May 8, 2024 at 3:22 AM Usama Arif <usamaarif642@xxxxxxxxx> wrote: > > Attempt writeback with the below steps and check using > memory.stat.zswpwb if zswap writeback occurred: > 1. Allocate memory. > 2. Reclaim memory equal to the amount that was allocated in step 1. > This will move it into zswap. > 3. Save current zswap usage. > 4. Move the memory allocated in step 1 back in from zswap. > 5. Set zswap.max to half the amount that was recorded in step 3. > 6. Attempt to reclaim memory equal to the amount that was allocated, > this will either trigger writeback if it's enabled, or reclamation > will fail if writeback is disabled as there isn't enough zswap > space. > > Suggested-by: Nhat Pham <nphamcs@xxxxxxxxx> > Signed-off-by: Usama Arif <usamaarif642@xxxxxxxxx> > --- > > v3 -> v4 (Yosry Ahmed): > - Use a fixed page-sized buffer for filling and checking memory when > attempting writeback > - Use cg_write_numeric instead of cg_write for memory.reclaim > - Improved error checking for zswpwb_before and zswpwb_after > > v2 -> v3: > - Remove memory.max (Yosry Ahmed) > - change from random allocation of memory to increasing and 0 allocation > (Yosry Ahmed) > - stricter error checking when writeback is disabled (Yosry Ahmed) > - Ensure zswpwb_before == 0 (Yosry Ahmed) > - Variable definition reorder, function name change (Yosry Ahmed) > > v1 -> v2: > - Change method of causing writeback from limit zswap to memory reclaim. > (Further described in commit message) (Yosry Ahmed) > - Document why using random memory (Nhat Pham) > --- > tools/testing/selftests/cgroup/test_zswap.c | 130 +++++++++++++++++++- > 1 file changed, 129 insertions(+), 1 deletion(-) > > diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c > index f0e488ed90d8..beab9b979957 100644 > --- a/tools/testing/selftests/cgroup/test_zswap.c > +++ b/tools/testing/selftests/cgroup/test_zswap.c > @@ -50,7 +50,7 @@ static int get_zswap_stored_pages(size_t *value) > return read_int("/sys/kernel/debug/zswap/stored_pages", value); > } > > -static int get_cg_wb_count(const char *cg) > +static long get_cg_wb_count(const char *cg) > { > return cg_read_key_long(cg, "memory.stat", "zswpwb"); > } > @@ -248,6 +248,132 @@ static int test_zswapin(const char *root) > return ret; > } > > +/* > + * Attempt writeback with the following steps: > + * 1. Allocate memory. > + * 2. Reclaim memory equal to the amount that was allocated in step 1. > + This will move it into zswap. > + * 3. Save current zswap usage. > + * 4. Move the memory allocated in step 1 back in from zswap. > + * 5. Set zswap.max to half the amount that was recorded in step 3. > + * 6. Attempt to reclaim memory equal to the amount that was allocated, > + this will either trigger writeback if it's enabled, or reclamation > + will fail if writeback is disabled as there isn't enough zswap space. > + */ > +static int attempt_writeback(const char *cgroup, void *arg) > +{ > + long pagesize = sysconf(_SC_PAGESIZE); > + char *test_group = arg; > + size_t memsize = MB(4); > + char buf[pagesize]; > + long zswap_usage; > + bool wb_enabled; > + int ret = -1; > + char *mem; > + > + wb_enabled = cg_read_long(test_group, "memory.zswap.writeback"); > + mem = (char *)malloc(memsize); > + if (!mem) > + return ret; > + > + /* > + * Fill half of each page with increasing data, and keep other > + * half empty, this will result in data that is still compressible > + * and ends up in zswap, with material zswap usage. > + */ > + for (int i = 0; i < pagesize; i++) > + buf[i] = i < pagesize/2 ? (char) i : 0; > + > + for (int i = 0; i < memsize; i += pagesize) > + memcpy(&mem[i], buf, pagesize); > + > + /* Try and reclaim allocated memory */ > + if (cg_write_numeric(test_group, "memory.reclaim", memsize)) { > + ksft_print_msg("Failed to reclaim all of the requested memory\n"); > + goto out; > + } > + > + zswap_usage = cg_read_long(test_group, "memory.zswap.current"); > + > + /* zswpin */ > + for (int i = 0; i < memsize; i += pagesize) { > + if (memcmp(&mem[i], buf, pagesize)) { > + ksft_print_msg("invalid memory\n"); > + goto out; > + } > + } > + > + if (cg_write_numeric(test_group, "memory.zswap.max", zswap_usage/2)) > + goto out; > + > + /* > + * If writeback is enabled, trying to reclaim memory now will trigger a > + * writeback as zswap.max is half of what was needed when reclaim ran the first time. > + * If writeback is disabled, memory reclaim will fail as zswap is limited and > + * it can't writeback to swap. > + */ > + ret = cg_write(test_group, "memory.reclaim", "4M"); We need cg_write_numeric(.., memsize) here as well. Otherwise, this LGTM. After fixing this, feel free to add: Acked-by: Yosry Ahmed <yosryahmed@xxxxxxxxxx> > + if (!wb_enabled) > + ret = (ret == -EAGAIN) ? 0 : -1; > + > +out: > + free(mem); > + return ret; > +} > + > +/* Test to verify the zswap writeback path */ > +static int test_zswap_writeback(const char *root, bool wb) > +{ > + long zswpwb_before, zswpwb_after; > + int ret = KSFT_FAIL; > + char *test_group; > + > + test_group = cg_name(root, "zswap_writeback_test"); > + if (!test_group) > + goto out; > + if (cg_create(test_group)) > + goto out; > + if (cg_write(test_group, "memory.zswap.writeback", wb ? "1" : "0")) > + goto out; > + > + zswpwb_before = get_cg_wb_count(test_group); > + if (zswpwb_before != 0) { > + ksft_print_msg("zswpwb_before = %ld instead of 0\n", zswpwb_before); > + goto out; > + } > + > + if (cg_run(test_group, attempt_writeback, (void *) test_group)) > + goto out; > + > + /* Verify that zswap writeback occurred only if writeback was enabled */ > + zswpwb_after = get_cg_wb_count(test_group); > + if (zswpwb_after < 0) > + goto out; > + > + if (wb != !!zswpwb_after) { > + ksft_print_msg("zswpwb_after is %ld while wb is %s", > + zswpwb_after, wb ? "enabled" : "disabled"); > + goto out; > + } > + > + ret = KSFT_PASS; > + > +out: > + cg_destroy(test_group); > + free(test_group); > + return ret; > +} > + > +static int test_zswap_writeback_enabled(const char *root) > +{ > + return test_zswap_writeback(root, true); > +} > + > +static int test_zswap_writeback_disabled(const char *root) > +{ > + return test_zswap_writeback(root, false); > +} > + > /* > * When trying to store a memcg page in zswap, if the memcg hits its memory > * limit in zswap, writeback should affect only the zswapped pages of that > @@ -425,6 +551,8 @@ struct zswap_test { > T(test_zswap_usage), > T(test_swapin_nozswap), > T(test_zswapin), > + T(test_zswap_writeback_enabled), > + T(test_zswap_writeback_disabled), > T(test_no_kmem_bypass), > T(test_no_invasive_cgroup_shrink), > }; > -- > 2.43.0 >