Merge tag 'for-6.3/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 24 Mar 2023 21:20:48 +0000 (14:20 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 24 Mar 2023 21:20:48 +0000 (14:20 -0700)
Pull device mapper fixes from Mike Snitzer:

 - Fix DM thin to work as a swap device by using 'limit_swap_bios' DM
   target flag (initially added to allow swap to dm-crypt) to throttle
   the amount of outstanding swap bios.

 - Fix DM crypt soft lockup warnings by calling cond_resched() from the
   cpu intensive loop in dmcrypt_write().

 - Fix DM crypt to not access an uninitialized tasklet. This fix allows
   for consistent handling of IO completion, by _not_ needlessly punting
   to a workqueue when tasklets are not needed.

 - Fix DM core's alloc_dev() initialization for DM stats to check for
   and propagate alloc_percpu() failure.

* tag 'for-6.3/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm stats: check for and propagate alloc_percpu failure
  dm crypt: avoid accessing uninitialized tasklet
  dm crypt: add cond_resched() to dmcrypt_write()
  dm thin: fix deadlock when swapping to thin device

1  2 
drivers/md/dm-crypt.c
drivers/md/dm.c

diff --combined drivers/md/dm-crypt.c
@@@ -72,7 -72,9 +72,9 @@@ struct dm_crypt_io 
        struct crypt_config *cc;
        struct bio *base_bio;
        u8 *integrity_metadata;
-       bool integrity_metadata_from_pool;
+       bool integrity_metadata_from_pool:1;
+       bool in_tasklet:1;
        struct work_struct work;
        struct tasklet_struct tasklet;
  
@@@ -1461,7 -1463,8 +1463,7 @@@ static int crypt_convert_block_skcipher
        return r;
  }
  
 -static void kcryptd_async_done(struct crypto_async_request *async_req,
 -                             int error);
 +static void kcryptd_async_done(void *async_req, int error);
  
  static int crypt_alloc_req_skcipher(struct crypt_config *cc,
                                     struct convert_context *ctx)
@@@ -1730,6 -1733,7 +1732,7 @@@ static void crypt_io_init(struct dm_cry
        io->ctx.r.req = NULL;
        io->integrity_metadata = NULL;
        io->integrity_metadata_from_pool = false;
+       io->in_tasklet = false;
        atomic_set(&io->io_pending, 0);
  }
  
@@@ -1776,14 -1780,13 +1779,13 @@@ static void crypt_dec_pending(struct dm
         * our tasklet. In this case we need to delay bio_endio()
         * execution to after the tasklet is done and dequeued.
         */
-       if (tasklet_trylock(&io->tasklet)) {
-               tasklet_unlock(&io->tasklet);
-               bio_endio(base_bio);
+       if (io->in_tasklet) {
+               INIT_WORK(&io->work, kcryptd_io_bio_endio);
+               queue_work(cc->io_queue, &io->work);
                return;
        }
  
-       INIT_WORK(&io->work, kcryptd_io_bio_endio);
-       queue_work(cc->io_queue, &io->work);
+       bio_endio(base_bio);
  }
  
  /*
@@@ -1936,6 -1939,7 +1938,7 @@@ pop_from_list
                        io = crypt_io_from_node(rb_first(&write_tree));
                        rb_erase(&io->rb_node, &write_tree);
                        kcryptd_io_write(io);
+                       cond_resched();
                } while (!RB_EMPTY_ROOT(&write_tree));
                blk_finish_plug(&plug);
        }
@@@ -2150,9 -2154,10 +2153,9 @@@ static void kcryptd_crypt_read_convert(
        crypt_dec_pending(io);
  }
  
 -static void kcryptd_async_done(struct crypto_async_request *async_req,
 -                             int error)
 +static void kcryptd_async_done(void *data, int error)
  {
 -      struct dm_crypt_request *dmreq = async_req->data;
 +      struct dm_crypt_request *dmreq = data;
        struct convert_context *ctx = dmreq->ctx;
        struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx);
        struct crypt_config *cc = io->cc;
@@@ -2230,6 -2235,7 +2233,7 @@@ static void kcryptd_queue_crypt(struct 
                 * it is being executed with irqs disabled.
                 */
                if (in_hardirq() || irqs_disabled()) {
+                       io->in_tasklet = true;
                        tasklet_init(&io->tasklet, kcryptd_crypt_tasklet, (unsigned long)&io->work);
                        tasklet_schedule(&io->tasklet);
                        return;
diff --combined drivers/md/dm.c
@@@ -512,10 -512,10 +512,10 @@@ static void dm_io_acct(struct dm_io *io
                sectors = io->sectors;
  
        if (!end)
 -              bdev_start_io_acct(bio->bi_bdev, sectors, bio_op(bio),
 -                                 start_time);
 +              bdev_start_io_acct(bio->bi_bdev, bio_op(bio), start_time);
        else
 -              bdev_end_io_acct(bio->bi_bdev, bio_op(bio), start_time);
 +              bdev_end_io_acct(bio->bi_bdev, bio_op(bio), sectors,
 +                               start_time);
  
        if (static_branch_unlikely(&stats_enabled) &&
            unlikely(dm_stats_used(&md->stats))) {
@@@ -2097,7 -2097,9 +2097,9 @@@ static struct mapped_device *alloc_dev(
        if (!md->pending_io)
                goto bad;
  
-       dm_stats_init(&md->stats);
+       r = dm_stats_init(&md->stats);
+       if (r < 0)
+               goto bad;
  
        /* Populate the mapping, nobody knows we exist yet */
        spin_lock(&_minor_lock);