[SRU][B][F][G][PATCH 1/1] dm crypt: add flags to optionally bypass kcryptd workqueues

Gerald Yang gerald.yang at canonical.com
Tue May 18 03:04:15 UTC 2021


I sent out the same SRU email twice, sorry about that
please check the first one and ignore the second one

Thanks,
Gerald

On Tue, May 18, 2021 at 11:00 AM Gerald Yang <gerald.yang at canonical.com>
wrote:

> From: Ignat Korchagin <ignat at cloudflare.com>
>
> BugLink: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1910976
>
> This is a follow up to [1] that detailed latency problems associated
> with dm-crypt's use of workqueues when processing IO.
>
> Current dm-crypt implementation creates a significant IO performance
> overhead (at least on small IO block sizes) for both latency and
> throughput. We suspect offloading IO request processing into
> workqueues and async threads is more harmful these days with the
> modern fast storage. I also did some digging into the dm-crypt git
> history and much of this async processing is not needed anymore,
> because the reasons it was added are mostly gone from the kernel. More
> details can be found in [2] (see "Git archeology" section).
>
> This change adds DM_CRYPT_NO_READ_WORKQUEUE and
> DM_CRYPT_NO_WRITE_WORKQUEUE flags for read and write BIOs, which
> direct dm-crypt to not offload crypto operations into kcryptd
> workqueues.  In addition, writes are not buffered to be sorted in the
> dm-crypt red-black tree, but dispatched immediately. For cases, where
> crypto operations cannot happen (hard interrupt context, for example
> the read path of some NVME drivers), we offload the work to a tasklet
> rather than a workqueue.
>
> These flags only ensure no async BIO processing in the dm-crypt
> module. It is worth noting that some Crypto API implementations may
> offload encryption into their own workqueues, which are independent of
> the dm-crypt and its configuration. However upon enabling these new
> flags dm-crypt will instruct Crypto API not to backlog crypto
> requests.
>
> To give an idea of the performance gains for certain workloads,
> consider the script, and results when tested against various
> devices, detailed here:
> https://www.redhat.com/archives/dm-devel/2020-July/msg00138.html
>
> [1]: https://www.spinics.net/lists/dm-crypt/msg07516.html
> [2]: https://blog.cloudflare.com/speeding-up-linux-disk-encryption/
>
> Signed-off-by: Ignat Korchagin <ignat at cloudflare.com>
> Reviewed-by: Damien Le Moal <damien.lemoal at wdc.com>
> Reviewed-by: Bob Liu <bob.liu at oracle.com>
> Signed-off-by: Mike Snitzer <snitzer at redhat.com>
> (cherry picked from commit 39d42fa96ba1b7d2544db3f8ed5da8fb0d5cb877)
> Signed-off-by: Gerald Yang <gerald.yang at canonical.com>
> ---
>  drivers/md/dm-crypt.c | 50 ++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 42 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
> index d85648b9c247..91aa99e1d698 100644
> --- a/drivers/md/dm-crypt.c
> +++ b/drivers/md/dm-crypt.c
> @@ -67,6 +67,7 @@ struct dm_crypt_io {
>         u8 *integrity_metadata;
>         bool integrity_metadata_from_pool;
>         struct work_struct work;
> +       struct tasklet_struct tasklet;
>
>         struct convert_context ctx;
>
> @@ -120,7 +121,8 @@ struct iv_tcw_private {
>   * and encrypts / decrypts at the same time.
>   */
>  enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
> -            DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD };
> +            DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD,
> +            DM_CRYPT_NO_READ_WORKQUEUE, DM_CRYPT_NO_WRITE_WORKQUEUE };
>
>  enum cipher_flags {
>         CRYPT_MODE_INTEGRITY_AEAD,      /* Use authenticated mode for
> cihper */
> @@ -1211,7 +1213,7 @@ static void crypt_free_req(struct crypt_config *cc,
> void *req, struct bio *base_
>   * Encrypt / decrypt data from one bio to another one (can be the same
> one)
>   */
>  static blk_status_t crypt_convert(struct crypt_config *cc,
> -                        struct convert_context *ctx)
> +                        struct convert_context *ctx, bool atomic)
>  {
>         unsigned int tag_offset = 0;
>         unsigned int sector_step = cc->sector_size >> SECTOR_SHIFT;
> @@ -1254,7 +1256,8 @@ static blk_status_t crypt_convert(struct
> crypt_config *cc,
>                         atomic_dec(&ctx->cc_pending);
>                         ctx->cc_sector += sector_step;
>                         tag_offset++;
> -                       cond_resched();
> +                       if (!atomic)
> +                               cond_resched();
>                         continue;
>                 /*
>                  * There was a data integrity error.
> @@ -1580,7 +1583,8 @@ static void kcryptd_crypt_write_io_submit(struct
> dm_crypt_io *io, int async)
>
>         clone->bi_iter.bi_sector = cc->start + io->sector;
>
> -       if (likely(!async) && test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags)) {
> +       if ((likely(!async) && test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags))
> ||
> +           test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags)) {
>                 generic_make_request(clone);
>                 return;
>         }
> @@ -1629,7 +1633,8 @@ static void kcryptd_crypt_write_convert(struct
> dm_crypt_io *io)
>         sector += bio_sectors(clone);
>
>         crypt_inc_pending(io);
> -       r = crypt_convert(cc, &io->ctx);
> +       r = crypt_convert(cc, &io->ctx,
> +                         test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE,
> &cc->flags));
>         if (r)
>                 io->error = r;
>         crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending);
> @@ -1659,7 +1664,8 @@ static void kcryptd_crypt_read_convert(struct
> dm_crypt_io *io)
>         crypt_convert_init(cc, &io->ctx, io->base_bio, io->base_bio,
>                            io->sector);
>
> -       r = crypt_convert(cc, &io->ctx);
> +       r = crypt_convert(cc, &io->ctx,
> +                         test_bit(DM_CRYPT_NO_READ_WORKQUEUE,
> &cc->flags));
>         if (r)
>                 io->error = r;
>
> @@ -1719,10 +1725,28 @@ static void kcryptd_crypt(struct work_struct *work)
>                 kcryptd_crypt_write_convert(io);
>  }
>
> +static void kcryptd_crypt_tasklet(unsigned long work)
> +{
> +       kcryptd_crypt((struct work_struct *)work);
> +}
> +
>  static void kcryptd_queue_crypt(struct dm_crypt_io *io)
>  {
>         struct crypt_config *cc = io->cc;
>
> +       if ((bio_data_dir(io->base_bio) == READ &&
> test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags)) ||
> +           (bio_data_dir(io->base_bio) == WRITE &&
> test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags))) {
> +               if (in_irq()) {
> +                       /* Crypto API's "skcipher_walk_first() refuses to
> work in hard IRQ context */
> +                       tasklet_init(&io->tasklet, kcryptd_crypt_tasklet,
> (unsigned long)&io->work);
> +                       tasklet_schedule(&io->tasklet);
> +                       return;
> +               }
> +
> +               kcryptd_crypt(&io->work);
> +               return;
> +       }
> +
>         INIT_WORK(&io->work, kcryptd_crypt);
>         queue_work(cc->crypt_queue, &io->work);
>  }
> @@ -2483,7 +2507,7 @@ static int crypt_ctr_optional(struct dm_target *ti,
> unsigned int argc, char **ar
>         struct crypt_config *cc = ti->private;
>         struct dm_arg_set as;
>         static const struct dm_arg _args[] = {
> -               {0, 6, "Invalid number of feature args"},
> +               {0, 8, "Invalid number of feature args"},
>         };
>         unsigned int opt_params, val;
>         const char *opt_string, *sval;
> @@ -2513,6 +2537,10 @@ static int crypt_ctr_optional(struct dm_target *ti,
> unsigned int argc, char **ar
>
>                 else if (!strcasecmp(opt_string, "submit_from_crypt_cpus"))
>                         set_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags);
> +               else if (!strcasecmp(opt_string, "no_read_workqueue"))
> +                       set_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags);
> +               else if (!strcasecmp(opt_string, "no_write_workqueue"))
> +                       set_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags);
>                 else if (sscanf(opt_string, "integrity:%u:", &val) == 1) {
>                         if (val == 0 || val > MAX_TAG_SIZE) {
>                                 ti->error = "Invalid integrity arguments";
> @@ -2842,6 +2870,8 @@ static void crypt_status(struct dm_target *ti,
> status_type_t type,
>                 num_feature_args += !!ti->num_discard_bios;
>                 num_feature_args += test_bit(DM_CRYPT_SAME_CPU,
> &cc->flags);
>                 num_feature_args += test_bit(DM_CRYPT_NO_OFFLOAD,
> &cc->flags);
> +               num_feature_args += test_bit(DM_CRYPT_NO_READ_WORKQUEUE,
> &cc->flags);
> +               num_feature_args += test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE,
> &cc->flags);
>                 num_feature_args += cc->sector_size != (1 << SECTOR_SHIFT);
>                 num_feature_args += test_bit(CRYPT_IV_LARGE_SECTORS,
> &cc->cipher_flags);
>                 if (cc->on_disk_tag_size)
> @@ -2854,6 +2884,10 @@ static void crypt_status(struct dm_target *ti,
> status_type_t type,
>                                 DMEMIT(" same_cpu_crypt");
>                         if (test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags))
>                                 DMEMIT(" submit_from_crypt_cpus");
> +                       if (test_bit(DM_CRYPT_NO_READ_WORKQUEUE,
> &cc->flags))
> +                               DMEMIT(" no_read_workqueue");
> +                       if (test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE,
> &cc->flags))
> +                               DMEMIT(" no_write_workqueue");
>                         if (cc->on_disk_tag_size)
>                                 DMEMIT(" integrity:%u:%s",
> cc->on_disk_tag_size, cc->cipher_auth);
>                         if (cc->sector_size != (1 << SECTOR_SHIFT))
> @@ -2966,7 +3000,7 @@ static void crypt_io_hints(struct dm_target *ti,
> struct queue_limits *limits)
>
>  static struct target_type crypt_target = {
>         .name   = "crypt",
> -       .version = {1, 19, 0},
> +       .version = {1, 22, 0},
>         .module = THIS_MODULE,
>         .ctr    = crypt_ctr,
>         .dtr    = crypt_dtr,
> --
> 2.25.1
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ubuntu.com/archives/kernel-team/attachments/20210518/045df7bf/attachment-0001.html>


More information about the kernel-team mailing list