]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
zram: permit only one post-processing operation at a time
authorSergey Senozhatsky <senozhatsky@chromium.org>
Tue, 17 Sep 2024 02:09:07 +0000 (11:09 +0900)
committerAndrew Morton <akpm@linux-foundation.org>
Wed, 6 Nov 2024 00:56:22 +0000 (16:56 -0800)
Both recompress and writeback soon will unlock slots during processing,
which makes things too complex wrt possible race-conditions.  We still
want to clear PP_SLOT in slot_free, because this is how we figure out that
slot that was selected for post-processing has been released under us and
when we start post-processing we check if slot still has PP_SLOT set.  At
the same time, theoretically, we can have something like this:

CPU0     CPU1

recompress
scan slots
set PP_SLOT
unlock slot
slot_free
clear PP_SLOT

allocate PP_SLOT
writeback
scan slots
set PP_SLOT
unlock slot
select PP-slot
test PP_SLOT

So recompress will not detect that slot has been re-used and re-selected
for concurrent writeback post-processing.

Make sure that we only permit on post-processing operation at a time.  So
now recompress and writeback post-processing don't race against each
other, we only need to handle slot re-use (slot_free and write), which is
handled individually by each pp operation.

Having recompress and writeback competing for the same slots is not
exactly good anyway (can't imagine anyone doing that).

Link: https://lkml.kernel.org/r/20240917021020.883356-3-senozhatsky@chromium.org
Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: Minchan Kim <minchan@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Documentation/admin-guide/blockdev/zram.rst
drivers/block/zram/zram_drv.c
drivers/block/zram/zram_drv.h

index 678d70d6e1c3ac6b3f442d0f7f03ac781532342b..714a5171bfc0b869212e1e156a8cf06e00ab4193 100644 (file)
@@ -47,6 +47,8 @@ The list of possible return codes:
 -ENOMEM          zram was not able to allocate enough memory to fulfil your
          needs.
 -EINVAL          invalid input has been provided.
+-EAGAIN          re-try operation later (e.g. when attempting to run recompress
+         and writeback simultaneously).
 ========  =============================================================
 
 If you use 'echo', the returned value is set by the 'echo' utility,
index d61750c1c5b5c3c9a8c4150b676beea04930191b..37a284f709ba9362381bb0034ac08e74486b0ddf 100644 (file)
@@ -627,6 +627,12 @@ static ssize_t writeback_store(struct device *dev,
                goto release_init_lock;
        }
 
+       /* Do not permit concurrent post-processing actions. */
+       if (atomic_xchg(&zram->pp_in_progress, 1)) {
+               up_read(&zram->init_lock);
+               return -EAGAIN;
+       }
+
        if (!zram->backing_dev) {
                ret = -ENODEV;
                goto release_init_lock;
@@ -753,6 +759,7 @@ next:
                free_block_bdev(zram, blk_idx);
        __free_page(page);
 release_init_lock:
+       atomic_set(&zram->pp_in_progress, 0);
        up_read(&zram->init_lock);
 
        return ret;
@@ -1883,6 +1890,12 @@ static ssize_t recompress_store(struct device *dev,
                goto release_init_lock;
        }
 
+       /* Do not permit concurrent post-processing actions. */
+       if (atomic_xchg(&zram->pp_in_progress, 1)) {
+               up_read(&zram->init_lock);
+               return -EAGAIN;
+       }
+
        if (algo) {
                bool found = false;
 
@@ -1950,6 +1963,7 @@ next:
        __free_page(page);
 
 release_init_lock:
+       atomic_set(&zram->pp_in_progress, 0);
        up_read(&zram->init_lock);
        return ret;
 }
@@ -2146,6 +2160,7 @@ static void zram_reset_device(struct zram *zram)
        zram->disksize = 0;
        zram_destroy_comps(zram);
        memset(&zram->stats, 0, sizeof(zram->stats));
+       atomic_set(&zram->pp_in_progress, 0);
        reset_bdev(zram);
 
        comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor);
@@ -2383,6 +2398,7 @@ static int zram_add(void)
        zram->disk->fops = &zram_devops;
        zram->disk->private_data = zram;
        snprintf(zram->disk->disk_name, 16, "zram%d", device_id);
+       atomic_set(&zram->pp_in_progress, 0);
 
        /* Actual capacity set using sysfs (/sys/block/zram<id>/disksize */
        set_capacity(zram->disk, 0);
index 914cb662996949a04ed273db697acfcfa2b820c9..73a9d47d76bae1edd6c025bf5c30f76136bba83d 100644 (file)
@@ -140,5 +140,6 @@ struct zram {
 #ifdef CONFIG_ZRAM_MEMORY_TRACKING
        struct dentry *debugfs_dir;
 #endif
+       atomic_t pp_in_progress;
 };
 #endif