Linux block layer
 help / color / mirror / Atom feed
* Re: [PATCH v2 1/3] scsi: scan: allocate sdev and starget on the NUMA node of the host adapter
From: Sumit Saxena @ 2026-04-28  7:22 UTC (permalink / raw)
  To: John Garry
  Cc: martin.petersen, axboe, linux-scsi, linux-block,
	mpi3mr-linuxdrv.pdl, James Rizzo
In-Reply-To: <4fa5c202-e884-41ce-8160-f3dcc289293f@oracle.com>

[-- Attachment #1: Type: text/plain, Size: 1067 bytes --]

On Mon, Apr 27, 2026 at 1:57 PM John Garry <john.g.garry@oracle.com> wrote:
>
> On 24/04/2026 12:33, Sumit Saxena wrote:
> >> For the actual shost allocation, we still use kzalloc() in
> >> scsi_host_alloc(). However, shost associated device is often a pci
> >> device, and we probe pci devices in the same NUMA node it exists, and we
> >> try NUMA local allocations by default, so nothing is needed to change
> >> for the shost allocation - is this right?
> > shost allocation is not an issue, I will drop changes related to it.
>
> But you have not made any changes related shost allocation and I am
> questioning why.
Yes, sorry for the confusion.
>
> Further to what I was saying, even though most shosts are based on PCI
> device, not all are and I think that it is worth making this same change
> for shost allocation.
Ok, I will add similar changes for shost allocation in v3.
>
> > The tests indicate that at times sdev and starget are allocated to
> > remote NUMA node.
> > So, I will limit these changes to sdev and starget only.
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]

^ permalink raw reply

* Re: [PATCH v5 1/2] blk-mq: add tracepoint block_rq_tag_wait
From: Aaron Tomlin @ 2026-04-28  0:29 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: axboe, mhiramat, mathieu.desnoyers, bvanassche,
	johannes.thumshirn, kch, dlemoal, ritesh.list, loberman, neelx,
	sean, mproche, chjohnst, linux-block, linux-kernel,
	linux-trace-kernel
In-Reply-To: <20260427123848.1e6b63d2@gandalf.local.home>

[-- Attachment #1: Type: text/plain, Size: 975 bytes --]

On Mon, Apr 27, 2026 at 12:38:48PM -0400, Steven Rostedt wrote:
> On Sun, 26 Apr 2026 22:01:41 -0400
> Aaron Tomlin <atomlin@atomlin.com> wrote:
> 
> > +TRACE_EVENT(block_rq_tag_wait,
> > +
> > +	TP_PROTO(struct request_queue *q, struct blk_mq_hw_ctx *hctx, bool is_sched_tag),
> > +
> > +	TP_ARGS(q, hctx, is_sched_tag),
> > +
> > +	TP_STRUCT__entry(
> > +		__field( dev_t,		dev			)
> > +		__field( u32,		hctx_id			)
> > +		__field( u32,		nr_tags			)
> > +		__field( bool,		is_sched_tag		)
> > +	),
> > +
> > +	TP_fast_assign(
> > +		__entry->dev		= q->disk ? disk_devt(q->disk);
> 
> Hmm, does the above even compile?
> 
Hi Steve,

	TP_fast_assign(
		__entry->dev		= q->disk ? disk_devt(q->disk) : 0;

I embarrassingly dropped ": 0" from the ternary operator while preparing
the v5 patch and failed to catch the syntax error before sending it out.

I will fix the syntax, verify the build and spin a v6.


Kind regards,
-- 
Aaron Tomlin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: [PATCH v2 2/3] dm-inlinecrypt: add target for inline block device encryption
From: Benjamin Marzinski @ 2026-04-27 23:21 UTC (permalink / raw)
  To: Linlin Zhang
  Cc: linux-block, ebiggers, mpatocka, gmazyland, linux-kernel,
	adrianvovk, dm-devel, quic_mdalam, israelr, hch, axboe
In-Reply-To: <ae7yzw-5wz9cqZt8@redhat.com>

On Mon, Apr 27, 2026 at 01:23:27AM -0400, Benjamin Marzinski wrote:
> On Fri, Apr 10, 2026 at 06:40:30AM -0700, Linlin Zhang wrote:
> > From: Eric Biggers <ebiggers@google.com>
> > +	/*
> > +	 * Since we've added an encryption context to the bio and
> > +	 * blk-crypto-fallback may be needed to process it, it's necessary to
> > +	 * use the fallback-aware bio submission code rather than
> > +	 * unconditionally returning DM_MAPIO_REMAPPED.
> > +	 *
> > +	 * To get the correct accounting for a dm target in the case where
> > +	 * __blk_crypto_submit_bio() doesn't take ownership of the bio (returns
> > +	 * true), call __blk_crypto_submit_bio() directly and return
> > +	 * DM_MAPIO_REMAPPED in that case, rather than relying on
> > +	 * blk_crypto_submit_bio() which calls submit_bio() in that case.
> > +	 */
> > +	if (__blk_crypto_submit_bio(bio))
> 
> This will still double account for fallback writes (which call
> submit_bio() on the encrypted bios, and return DM_MAPIO_SUBMITTED here). 

Just to clarify, I'm talking about the vmstats accounting. The IO
originally gets accounted by submit_bio() when the bio is submitted to
the dm device. For actual inline encryption and fallback reads, dm will
submit the bio to the underlying device using submit_bio_noacct() to
avoid double-counting the IO.

For fallback writes, __blk_crypto_submit_bio() will submit the encrypted
bios to the underlying device with submit_bio(). This adds the IO
sectors again, even though it's the same IO, only encrypted now.

-Ben

> 
> -Ben
> 
> > +		return DM_MAPIO_REMAPPED;
> > +	return DM_MAPIO_SUBMITTED;
> > +}
> 


^ permalink raw reply

* Re: [PATCH 0/1] cdrom, sr: patch for inclusion
From: Jens Axboe @ 2026-04-27 21:52 UTC (permalink / raw)
  To: Phillip Potter; +Cc: linux-block, daan
In-Reply-To: <20260427210139.1400-1-phil@philpotter.co.uk>


On Mon, 27 Apr 2026 22:01:38 +0100, Phillip Potter wrote:
> Please apply the following patch by Daan De Meyer from Amutable, which
> reworks some code to correct a longstanding issue with propagation of
> writable status to the block layer. This has been reviewed both by
> myself and Martin K. Petersen of Oracle, and I have also built and
> runtime tested it without issue.
> 
> Many thanks in advance.
> 
> [...]

Applied, thanks!

[1/1] cdrom, scsi: sr: propagate read-only status to block layer via set_disk_ro()
      commit: d1a1c3ecee38c81d0447fc0a06b0a2068d1f099a

Best regards,
-- 
Jens Axboe




^ permalink raw reply

* Re: [PATCH 1/1] cdrom, scsi: sr: propagate read-only status to block layer via set_disk_ro()
From: Jens Axboe @ 2026-04-27 21:52 UTC (permalink / raw)
  To: Phillip Potter; +Cc: linux-block, daan
In-Reply-To: <20260427210139.1400-2-phil@philpotter.co.uk>

On 4/27/26 3:01 PM, Phillip Potter wrote:
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Daan De Meyer <daan@amutable.com>
> Link: https://lore.kernel.org/all/20260422113206.246267-1-daan@amutable.com
> Reviewed-by: Phillip Potter <phil@philpotter.co.uk>
> Link: https://lore.kernel.org/all/ae59luYMh6npxD09@equinox
> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
> Link: https://lore.kernel.org/all/yq1jytti447.fsf@ca-mkp.ca.oracle.com
> Signed-off-by: Phillip Potter <phil@philpotter.co.uk>

A few notes:

- Please don't include a ton of Link tags in the commit message. Don't
  add anything unless it's a link to a bug report, and your patch fixes
  that bug.

- Signed-off-by implies Reviewed-by, you don't need both of those.

-- 
Jens Axboe

^ permalink raw reply

* [PATCH 1/1] cdrom, scsi: sr: propagate read-only status to block layer via set_disk_ro()
From: Phillip Potter @ 2026-04-27 21:01 UTC (permalink / raw)
  To: axboe; +Cc: linux-block, daan
In-Reply-To: <20260427210139.1400-1-phil@philpotter.co.uk>

From: Daan De Meyer <daan@amutable.com>

The cdrom core never calls set_disk_ro() for a registered device, so
BLKROGET on a CD-ROM device always returns 0 (writable), even when the
drive has no write capabilities and writes will inevitably fail. This
causes problems for userspace that relies on BLKROGET to determine
whether a block device is read-only. For example, systemd's loop device
setup uses BLKROGET to decide whether to create a loop device with
LO_FLAGS_READ_ONLY. Without the read-only flag, writes pass through the
loop device to the CD-ROM and fail with I/O errors. systemd-fsck
similarly checks BLKROGET to decide whether to run fsck in no-repair
mode (-n).

The write-capability bits in cdi->mask come from two different sources:
CDC_DVD_RAM and CDC_CD_RW are populated by the driver from the MODE
SENSE capabilities page (page 0x2A) before register_cdrom() is called,
while CDC_MRW_W and CDC_RAM require the MMC GET CONFIGURATION command
and were only probed by cdrom_open_write() at device open time. This
meant that any attempt to compute the writable state from the full
mask at probe time was incorrect, because the GET CONFIGURATION bits
were still unset (and cdi->mask is initialized such that capabilities
are assumed present).

Fix this by factoring the GET CONFIGURATION probing out of
cdrom_open_write() into a new exported helper,
cdrom_probe_write_features(), and having sr call it from sr_probe()
right after get_capabilities() has populated the MODE SENSE bits.
register_cdrom() then calls set_disk_ro() based on the full
write-capability mask (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)
so the block layer reflects the drive's actual write support. The
feature queries used (CDF_MRW and CDF_RWRT via GET CONFIGURATION with
RT=00) report drive-level capabilities that are persistent across
media, so a single probe before register_cdrom() is sufficient and the
redundant probe at open time is dropped.

With set_disk_ro() now accurate, the long-vestigial cd->writeable flag
in sr can go: get_capabilities() used to set cd->writeable based on
the same four mask bits, but because CDC_MRW_W and CDC_RAM default to
"capability present" in cdi->mask and aren't touched by MODE SENSE,
the condition that gated cd->writeable was always true, making it
unconditionally 1. Replace the corresponding gate in sr_init_command()
with get_disk_ro(cd->disk), which turns a previously no-op check into
a real one and also catches kernel-internal bio writers that bypass
blkdev_write_iter()'s bdev_read_only() check.

The sd driver (SCSI disks) does not have this problem because it
checks the MODE SENSE Write Protect bit and calls set_disk_ro()
accordingly. The sr driver cannot use the same approach because the
MMC specification does not define the WP bit in the MODE SENSE
device-specific parameter byte for CD-ROM devices.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Daan De Meyer <daan@amutable.com>
Link: https://lore.kernel.org/all/20260422113206.246267-1-daan@amutable.com
Reviewed-by: Phillip Potter <phil@philpotter.co.uk>
Link: https://lore.kernel.org/all/ae59luYMh6npxD09@equinox
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Link: https://lore.kernel.org/all/yq1jytti447.fsf@ca-mkp.ca.oracle.com
Signed-off-by: Phillip Potter <phil@philpotter.co.uk>
---
 drivers/cdrom/cdrom.c | 73 ++++++++++++++++++++++++++++---------------
 drivers/scsi/sr.c     | 11 ++-----
 drivers/scsi/sr.h     |  1 -
 include/linux/cdrom.h |  1 +
 4 files changed, 51 insertions(+), 35 deletions(-)

diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index fc049612d6dc..62934cf4b10d 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -631,6 +631,16 @@ int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi)
 
 	WARN_ON(!cdo->generic_packet);
 
+	/*
+	 * Propagate the drive's write support to the block layer so BLKROGET
+	 * reflects actual write capability. Drivers that use GET CONFIGURATION
+	 * features (CDC_MRW_W, CDC_RAM) must have called
+	 * cdrom_probe_write_features() before register_cdrom() so the mask is
+	 * complete here.
+	 */
+	set_disk_ro(disk, !CDROM_CAN(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM |
+				     CDC_CD_RW));
+
 	cd_dbg(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
 	mutex_lock(&cdrom_mutex);
 	list_add(&cdi->list, &cdrom_list);
@@ -742,6 +752,44 @@ static int cdrom_is_random_writable(struct cdrom_device_info *cdi, int *write)
 	return 0;
 }
 
+/*
+ * Probe write-related MMC features via GET CONFIGURATION and update
+ * cdi->mask accordingly. Drivers that populate cdi->mask from the MODE SENSE
+ * capabilities page (e.g. sr) should call this after those MODE SENSE bits
+ * have been set but before register_cdrom(), so that the full set of
+ * write-capability bits is known by the time register_cdrom() decides on the
+ * initial read-only state of the disk.
+ */
+void cdrom_probe_write_features(struct cdrom_device_info *cdi)
+{
+	int mrw, mrw_write, ram_write;
+
+	mrw = 0;
+	if (!cdrom_is_mrw(cdi, &mrw_write))
+		mrw = 1;
+
+	if (CDROM_CAN(CDC_MO_DRIVE))
+		ram_write = 1;
+	else
+		(void) cdrom_is_random_writable(cdi, &ram_write);
+
+	if (mrw)
+		cdi->mask &= ~CDC_MRW;
+	else
+		cdi->mask |= CDC_MRW;
+
+	if (mrw_write)
+		cdi->mask &= ~CDC_MRW_W;
+	else
+		cdi->mask |= CDC_MRW_W;
+
+	if (ram_write)
+		cdi->mask &= ~CDC_RAM;
+	else
+		cdi->mask |= CDC_RAM;
+}
+EXPORT_SYMBOL(cdrom_probe_write_features);
+
 static int cdrom_media_erasable(struct cdrom_device_info *cdi)
 {
 	disc_information di;
@@ -894,33 +942,8 @@ static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi)
  */
 static int cdrom_open_write(struct cdrom_device_info *cdi)
 {
-	int mrw, mrw_write, ram_write;
 	int ret = 1;
 
-	mrw = 0;
-	if (!cdrom_is_mrw(cdi, &mrw_write))
-		mrw = 1;
-
-	if (CDROM_CAN(CDC_MO_DRIVE))
-		ram_write = 1;
-	else
-		(void) cdrom_is_random_writable(cdi, &ram_write);
-	
-	if (mrw)
-		cdi->mask &= ~CDC_MRW;
-	else
-		cdi->mask |= CDC_MRW;
-
-	if (mrw_write)
-		cdi->mask &= ~CDC_MRW_W;
-	else
-		cdi->mask |= CDC_MRW_W;
-
-	if (ram_write)
-		cdi->mask &= ~CDC_RAM;
-	else
-		cdi->mask |= CDC_RAM;
-
 	if (CDROM_CAN(CDC_MRW_W))
 		ret = cdrom_mrw_open_write(cdi);
 	else if (CDROM_CAN(CDC_DVD_RAM))
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 7adb2573f50d..c36c54ecd354 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -395,7 +395,7 @@ static blk_status_t sr_init_command(struct scsi_cmnd *SCpnt)
 
 	switch (req_op(rq)) {
 	case REQ_OP_WRITE:
-		if (!cd->writeable)
+		if (get_disk_ro(cd->disk))
 			goto out;
 		SCpnt->cmnd[0] = WRITE_10;
 		cd->cdi.media_written = 1;
@@ -681,6 +681,7 @@ static int sr_probe(struct scsi_device *sdev)
 	error = -ENOMEM;
 	if (get_capabilities(cd))
 		goto fail_minor;
+	cdrom_probe_write_features(&cd->cdi);
 	sr_vendor_init(cd);
 
 	set_capacity(disk, cd->capacity);
@@ -899,14 +900,6 @@ static int get_capabilities(struct scsi_cd *cd)
 	/*else    I don't think it can close its tray
 		cd->cdi.mask |= CDC_CLOSE_TRAY; */
 
-	/*
-	 * if DVD-RAM, MRW-W or CD-RW, we are randomly writable
-	 */
-	if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
-			(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
-		cd->writeable = 1;
-	}
-
 	kfree(buffer);
 	return 0;
 }
diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h
index dc899277b3a4..2d92f9cb6fec 100644
--- a/drivers/scsi/sr.h
+++ b/drivers/scsi/sr.h
@@ -35,7 +35,6 @@ typedef struct scsi_cd {
 	struct scsi_device *device;
 	unsigned int vendor;	/* vendor code, see sr_vendor.c         */
 	unsigned long ms_offset;	/* for reading multisession-CD's        */
-	unsigned writeable : 1;
 	unsigned use:1;		/* is this device still supportable     */
 	unsigned xa_flag:1;	/* CD has XA sectors ? */
 	unsigned readcd_known:1;	/* drive supports READ_CD (0xbe) */
diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h
index b907e6c2307d..260d7968cf72 100644
--- a/include/linux/cdrom.h
+++ b/include/linux/cdrom.h
@@ -108,6 +108,7 @@ int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev,
 extern unsigned int cdrom_check_events(struct cdrom_device_info *cdi,
 				       unsigned int clearing);
 
+extern void cdrom_probe_write_features(struct cdrom_device_info *cdi);
 extern int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi);
 extern void unregister_cdrom(struct cdrom_device_info *cdi);
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH 0/1] cdrom, sr: patch for inclusion
From: Phillip Potter @ 2026-04-27 21:01 UTC (permalink / raw)
  To: axboe; +Cc: linux-block, daan

Hi Jens,

Please apply the following patch by Daan De Meyer from Amutable, which
reworks some code to correct a longstanding issue with propagation of
writable status to the block layer. This has been reviewed both by
myself and Martin K. Petersen of Oracle, and I have also built and
runtime tested it without issue.

Many thanks in advance.

Regards,
Phil

Daan De Meyer (1):
  cdrom, scsi: sr: propagate read-only status to block layer via
    set_disk_ro()

 drivers/cdrom/cdrom.c | 73 ++++++++++++++++++++++++++++---------------
 drivers/scsi/sr.c     | 11 ++-----
 drivers/scsi/sr.h     |  1 -
 include/linux/cdrom.h |  1 +
 4 files changed, 51 insertions(+), 35 deletions(-)

-- 
2.53.0


^ permalink raw reply

* Re: [RFC PATCH v2 0/4] mm/zsmalloc: reduce zs_free() latency on swap release path
From: Yosry Ahmed @ 2026-04-27 18:17 UTC (permalink / raw)
  To: Wenchao Hao
  Cc: Andrew Morton, Chengming Zhou, Jens Axboe, Johannes Weiner,
	Minchan Kim, Nhat Pham, Sergey Senozhatsky, linux-block,
	linux-kernel, linux-mm, Barry Song, Xueyuan Chen, Wenchao Hao
In-Reply-To: <CAOptpSPs-1UrEa8AHg19e590=SiV6bpnex7gCbif8=aY7BtpuA@mail.gmail.com>

On Sat, Apr 25, 2026 at 9:13 PM Wenchao Hao <haowenchao22@gmail.com> wrote:
>
> On Tue, Apr 21, 2026 at 8:16 PM Wenchao Hao <haowenchao22@gmail.com> wrote:
> >
> > Swap freeing can be expensive when unmapping a VMA containing
> > many swap entries. This has been reported to significantly
> > delay memory reclamation during Android's low-memory killing,
> > especially when multiple processes are terminated to free
> > memory, with slot_free() accounting for more than 80% of
> > the total cost of freeing swap entries.
> >
> > Two earlier attempts by Lei and Zhiguo added a new thread in the mm core
> > to asynchronously collect and free swap entries [1][2], but the
> > design itself is fairly complex.
> >
> Hi Nhat, Kairui, Barry, Xueyuan,
>
> Thanks for the review. I agree with the direction and have some ideas for
> an alternative approach.
>
> My approach: first eliminate pool->lock from zs_free() itself, then defer
> free to per-cpu buffers with a lockless handoff, and finally reduce
> class->lock overhead during drain by exploiting natural class locality.
> Achieving both per-cpu and per-class is difficult, so the class->lock
> optimization is a compromise — but one that works well in practice.
>
> 1. Encode class_idx in obj to eliminate pool->lock
>
> OBJ_INDEX_BITS is over-provisioned on 64-bit. For example on arm64
> (chain_size=8): OBJ_INDEX_BITS=24 but only 10 bits are actually needed
> for obj_idx, leaving 14 spare bits.
> We can split OBJ_INDEX into class_idx + obj_idx:
>
>     obj: [PFN | class_idx (OBJ_CLASS_BITS) | obj_idx (OBJ_IDX_BITS)]
>
> OBJ_CLASS_BITS is computed dynamically as `ilog2(ZS_SIZE_CLASSES - 1) + 1`
> (8 bits for 4K pages, 9 for 64K).
> Since class_idx is invariant across migration (only PFN changes), zs_free()
> can extract class_idx locklessly, then acquire class->lock and re-read obj for a
> stable PFN. No pool->lock needed.

How much of the benefit do we get with just these locking improvements
without having to defer any of the freeing work?

As others have pointed out, I don't want to just defer expensive work
without understanding why it's expensive and running into limitations
about why it cannot be improved without deferring.

^ permalink raw reply

* Re: [PATCH v5 1/2] blk-mq: add tracepoint block_rq_tag_wait
From: Steven Rostedt @ 2026-04-27 16:38 UTC (permalink / raw)
  To: Aaron Tomlin
  Cc: axboe, mhiramat, mathieu.desnoyers, bvanassche,
	johannes.thumshirn, kch, dlemoal, ritesh.list, loberman, neelx,
	sean, mproche, chjohnst, linux-block, linux-kernel,
	linux-trace-kernel
In-Reply-To: <20260427020142.358912-2-atomlin@atomlin.com>

On Sun, 26 Apr 2026 22:01:41 -0400
Aaron Tomlin <atomlin@atomlin.com> wrote:

> +TRACE_EVENT(block_rq_tag_wait,
> +
> +	TP_PROTO(struct request_queue *q, struct blk_mq_hw_ctx *hctx, bool is_sched_tag),
> +
> +	TP_ARGS(q, hctx, is_sched_tag),
> +
> +	TP_STRUCT__entry(
> +		__field( dev_t,		dev			)
> +		__field( u32,		hctx_id			)
> +		__field( u32,		nr_tags			)
> +		__field( bool,		is_sched_tag		)
> +	),
> +
> +	TP_fast_assign(
> +		__entry->dev		= q->disk ? disk_devt(q->disk);

Hmm, does the above even compile?

-- Steve

> +		__entry->hctx_id	= hctx->queue_num;
> +		__entry->is_sched_tag	= is_sched_tag;
> +
> +		if (is_sched_tag)
> +			__entry->nr_tags = hctx->sched_tags->nr_tags;
> +		else
> +			__entry->nr_tags = hctx->tags->nr_tags;
> +	),
> +
> +	TP_printk("%d,%d hctx=%u starved on %s tags (depth=%u)",
> +		  MAJOR(__entry->dev), MINOR(__entry->dev),
> +		  __entry->hctx_id,
> +		  __entry->is_sched_tag ? "scheduler" : "hardware",
> +		  __entry->nr_tags)
> +);
> +

^ permalink raw reply

* Re: [PATCH v12 05/13] blk-mq: add blk_mq_{online|possible}_queue_affinity
From: Sebastian Andrzej Siewior @ 2026-04-27 15:34 UTC (permalink / raw)
  To: Aaron Tomlin
  Cc: axboe, kbusch, hch, sagi, mst, aacraid, James.Bottomley,
	martin.petersen, liyihang9, kashyap.desai, sumit.saxena,
	shivasharan.srikanteshwara, chandrakanth.patil, sathya.prakash,
	sreekanth.reddy, suganath-prabu.subramani, ranjan.kumar,
	jinpu.wang, tglx, mingo, peterz, juri.lelli, vincent.guittot,
	akpm, maz, ruanjinjie, yphbchou0911, wagi, frederic, longman,
	chenridong, hare, kch, ming.lei, tom.leiming, steve, sean,
	chjohnst, neelx, mproche, nick.lange, marco.crivellari,
	linux-block, linux-kernel, virtualization, linux-nvme, linux-scsi,
	megaraidlinux.pdl, mpi3mr-linuxdrv.pdl, MPT-FusionLinux.pdl
In-Reply-To: <20260422185215.100929-6-atomlin@atomlin.com>

On 2026-04-22 14:52:07 [-0400], Aaron Tomlin wrote:
> From: Daniel Wagner <wagi@kernel.org>
> 
> Introduce blk_mq_{online|possible}_queue_affinity, which returns the
> queue-to-CPU mapping constraints defined by the block layer. This allows
> other subsystems (e.g., IRQ affinity setup) to respect block layer
> requirements.
> 
> It is necessary to provide versions for both the online and possible CPU
> masks because some drivers want to spread their I/O queues only across
> online CPUs, while others prefer to use all possible CPUs. And the mask
> used needs to match with the number of queues requested
> (see blk_num_{online|possible}_queues).

Which driver uses cpu_possible_mask? This mask is assigned at boot time
once the kernel figured how many CPUs are possible based on ACPI or
whatever the system uses. This mask does not change.

I only see drivers/scsi/lpfc/lpfc_init.c using it. Looking at
cpu_possible_mask might not be the right thing. It is usually the same
thing as "online" except on system where ACPI thinks that something
could be added via hotplug _or_ if the admin shuts down a CPU via
cpuhotplug _or_ boots with less (there a command line option for that). 

In case cpu_possible_mask != cpu_online_mask the intention is to
allocate memory and setup irqs for the offline CPUs?

> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> Reviewed-by: Hannes Reinecke <hare@suse.de>
> Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>

Sebastian

^ permalink raw reply

* Re: [PATCH] block: rbd: switch to dynamic root device
From: Johan Hovold @ 2026-04-27 15:21 UTC (permalink / raw)
  To: Viacheslav Dubeyko
  Cc: idryomov@gmail.com, dongsheng.yang@linux.dev,
	ceph-devel@vger.kernel.org, axboe@kernel.dk,
	linux-kernel@vger.kernel.org, linux-block@vger.kernel.org
In-Reply-To: <ac8ef2a52e42512d17ef3e29cc94353c191c6846.camel@ibm.com>

On Fri, Apr 24, 2026 at 06:48:32PM +0000, Viacheslav Dubeyko wrote:
> On Fri, 2026-04-24 at 12:39 +0200, Johan Hovold wrote:
> > Driver core expects devices to be dynamically allocated and will, for
> > example, complain loudly when no release function has been provided.
> > 
> > Use root_device_register() to allocate and register the root device
> > instead of open coding using a static device.

> > @@ -5390,7 +5383,7 @@ static struct rbd_device *__rbd_dev_create(struct rbd_spec *spec)
> >  
> >  	rbd_dev->dev.bus = &rbd_bus_type;
> >  	rbd_dev->dev.type = &rbd_device_type;
> > -	rbd_dev->dev.parent = &rbd_root_dev;
> > +	rbd_dev->dev.parent = rbd_root_dev;
> >  	device_initialize(&rbd_dev->dev);
> >  
> >  	return rbd_dev;
> > @@ -7331,15 +7324,13 @@ static int __init rbd_sysfs_init(void)
> >  {
> >  	int ret;
> >  
> > -	ret = device_register(&rbd_root_dev);
> > -	if (ret < 0) {
> > -		put_device(&rbd_root_dev);
> > -		return ret;
> > -	}
> > +	rbd_root_dev = root_device_register("rbd");
> > +	if (IS_ERR(rbd_root_dev))
> > +		return PTR_ERR(rbd_root_dev);
> >  
> >  	ret = bus_register(&rbd_bus_type);
> >  	if (ret < 0)
> > -		device_unregister(&rbd_root_dev);
> > +		root_device_unregister(rbd_root_dev);
> 
> I think we need to assign NULL here:
> 
> rbd_root_dev = NULL;

From what I can tell these devices are only created from the bus sysfs
attribute so this pointer will not be accessed if bus registration
fails and there is no need to clear it (or add a sanity check to
__rbd_dev_create()).

> >  	return ret;
> >  }
> > @@ -7347,7 +7338,7 @@ static int __init rbd_sysfs_init(void)
> >  static void __exit rbd_sysfs_cleanup(void)
> >  {
> >  	bus_unregister(&rbd_bus_type);
> > -	device_unregister(&rbd_root_dev);
> > +	root_device_unregister(rbd_root_dev);
> 
> The same issue here:
> 
> rbd_root_dev = NULL;

So this is not needed either.

Johan

^ permalink raw reply

* Re: [PATCH v12 02/13] lib/group_cpus: remove dead !SMP code
From: Sebastian Andrzej Siewior @ 2026-04-27 15:21 UTC (permalink / raw)
  To: Aaron Tomlin
  Cc: axboe, kbusch, hch, sagi, mst, aacraid, James.Bottomley,
	martin.petersen, liyihang9, kashyap.desai, sumit.saxena,
	shivasharan.srikanteshwara, chandrakanth.patil, sathya.prakash,
	sreekanth.reddy, suganath-prabu.subramani, ranjan.kumar,
	jinpu.wang, tglx, mingo, peterz, juri.lelli, vincent.guittot,
	akpm, maz, ruanjinjie, yphbchou0911, wagi, frederic, longman,
	chenridong, hare, kch, ming.lei, tom.leiming, steve, sean,
	chjohnst, neelx, mproche, nick.lange, marco.crivellari,
	linux-block, linux-kernel, virtualization, linux-nvme, linux-scsi,
	megaraidlinux.pdl, mpi3mr-linuxdrv.pdl, MPT-FusionLinux.pdl
In-Reply-To: <20260422185215.100929-3-atomlin@atomlin.com>

On 2026-04-22 14:52:04 [-0400], Aaron Tomlin wrote:
> From: Daniel Wagner <wagi@kernel.org>
> 
> The support for the !SMP configuration has been removed from the core by
> commit cac5cefbade9 ("sched/smp: Make SMP unconditional").
> 
> While one can technically still compile a uniprocessor kernel, the core
> scheduler now mandates SMP unconditionally, rendering this particular
> !SMP fallback handling redundant. Therefore, remove the #ifdef CONFIG_SMP
> guards and the fallback logic.
> 
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
> Reviewed-by: Hannes Reinecke <hare@suse.de>
> [atomlin: Updated commit message to clarify !SMP removal context]

This look unchanged vs previous submission. You could explain why you
want to remove the !SMP case. It looks like the !SMP makes things
easier ;) I don't know how much of this gets removed because of !SMP
code elsewhere.

The description still does not make sense/ is accurate.

> Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
> ---
>  lib/group_cpus.c | 20 --------------------
>  1 file changed, 20 deletions(-)
> 
> diff --git a/lib/group_cpus.c b/lib/group_cpus.c
> index e6e18d7a49bb..b8d54398f88a 100644
> --- a/lib/group_cpus.c
> +++ b/lib/group_cpus.c
> @@ -9,8 +9,6 @@
>  #include <linux/sort.h>
>  #include <linux/group_cpus.h>
>  
> -#ifdef CONFIG_SMP
> -
>  static void grp_spread_init_one(struct cpumask *irqmsk, struct cpumask *nmsk,
>  				unsigned int cpus_per_grp)
>  {
> @@ -564,22 +562,4 @@ struct cpumask *group_cpus_evenly(unsigned int numgrps, unsigned int *nummasks)
>  	*nummasks = min(nr_present + nr_others, numgrps);
>  	return masks;
>  }
> -#else /* CONFIG_SMP */
> -struct cpumask *group_cpus_evenly(unsigned int numgrps, unsigned int *nummasks)
> -{
> -	struct cpumask *masks;
> -
> -	if (numgrps == 0)
> -		return NULL;
> -
> -	masks = kzalloc_objs(*masks, numgrps);
> -	if (!masks)
> -		return NULL;
> -
> -	/* assign all CPUs(cpu 0) to the 1st group only */
> -	cpumask_copy(&masks[0], cpu_possible_mask);
> -	*nummasks = 1;
> -	return masks;
> -}
> -#endif /* CONFIG_SMP */
>  EXPORT_SYMBOL_GPL(group_cpus_evenly);

Sebastian

^ permalink raw reply

* [PATCH v4] badblocks: fix infinite loop due to incorrect rounding and overflow
From: Ramesh Adhikari @ 2026-04-27 15:17 UTC (permalink / raw)
  To: axboe, gregkh; +Cc: linux-block, Ramesh Adhikari

The roundup() and rounddown() macros return the rounded value but
do not modify the input in place. In _badblocks_set(), _badblocks_clear(),
and badblocks_check(), the return values were being discarded, causing
s and target/next to remain unrounded. This resulted in sectors
being calculated from unrounded values, which could lead to sectors
being way too large (or zero), causing infinite loops in the
re_insert/re_clear/re_check loops.

Additionally, add integer overflow checks (s > ULLONG_MAX - sectors)
before the s + sectors calculation in all three functions to prevent
overflow-related issues. Also add early return when sectors becomes
zero after rounding in badblocks_check().

Root cause: When s and sectors have specific values (e.g., from
syzkaller fuzzing via nvdimm ioctl), the unrounded values cause
sectors to be incorrectly calculated. In _badblocks_clear(), this
could result in needing 2^46 iterations to process 2^55 sectors,
triggering RCU stall warnings and effectively hanging the kernel.

Fix by properly capturing the return values from roundup() and
rounddown(), adding overflow checks before sector arithmetic, and
handling the zero-sectors case in badblocks_check().

Signed-off-by: Ramesh Adhikari <adhikari.resume@gmail.com>
---
Changes in v4:
- Complete rewrite of the fix approach
- Previous v1-v3 incorrectly addressed len==0 as symptoms
- Root cause identified: roundup()/rounddown() return values discarded
- Fixed across all three functions (_badblocks_set, _badblocks_clear, badblocks_check)
- Added ULLONG_MAX overflow checks before sector arithmetic
- Added proper sectors==0 early return after rounding
 block/badblocks.c | 43 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/block/badblocks.c b/block/badblocks.c
index ece64e76fe8..a5ffae65a05 100644
--- a/block/badblocks.c
+++ b/block/badblocks.c
@@ -855,13 +855,21 @@ static bool _badblocks_set(struct badblocks *bb, sector_t s, sector_t sectors,
 
 	if (bb->shift) {
 		/* round the start down, and the end up */
+		if (s > ULLONG_MAX - sectors)
+			return false;
 		sector_t next = s + sectors;
 
-		rounddown(s, 1 << bb->shift);
-		roundup(next, 1 << bb->shift);
-		sectors = next - s;
+		s = rounddown(s, 1 << bb->shift);
+		next = roundup(next, 1 << bb->shift);
+		if (next < s)
+			sectors = 0;
+		else
+			sectors = next - s;
 	}
 
+	if (sectors == 0)
+		return false;
+
 	write_seqlock_irqsave(&bb->lock, flags);
 
 	bad.ack = acknowledged;
@@ -1070,12 +1078,20 @@ static bool _badblocks_clear(struct badblocks *bb, sector_t s, sector_t sectors)
 		 * However it is better the think a block is bad when it
 		 * isn't than to think a block is not bad when it is.
 		 */
+		if (s > ULLONG_MAX - sectors)
+			return false;
 		target = s + sectors;
-		roundup(s, 1 << bb->shift);
-		rounddown(target, 1 << bb->shift);
-		sectors = target - s;
+		s = roundup(s, 1 << bb->shift);
+		target = rounddown(target, 1 << bb->shift);
+		if (target < s)
+			sectors = 0;
+		else
+			sectors = target - s;
 	}
 
+	if (sectors == 0)
+		return false;
+
 	write_seqlock_irq(&bb->lock);
 
 	bad.ack = true;
@@ -1305,11 +1321,20 @@ int badblocks_check(struct badblocks *bb, sector_t s, sector_t sectors,
 
 	if (bb->shift > 0) {
 		/* round the start down, and the end up */
+		if (s > ULLONG_MAX - sectors) {
+			return -EINVAL;
+		}
 		sector_t target = s + sectors;
 
-		rounddown(s, 1 << bb->shift);
-		roundup(target, 1 << bb->shift);
-		sectors = target - s;
+		s = rounddown(s, 1 << bb->shift);
+		target = roundup(target, 1 << bb->shift);
+		if (target < s)
+			sectors = 0;
+		else
+			sectors = target - s;
+
+		if (sectors == 0)
+			return 0;
 	}
 
 retry:
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] badblocks: fix infinite loop due to incorrect rounding and overflow
From: Greg KH @ 2026-04-27 15:12 UTC (permalink / raw)
  To: Ramesh Adhikari; +Cc: axboe, linux-block
In-Reply-To: <20260427151048.756072-1-adhikari.resume@gmail.com>

On Mon, Apr 27, 2026 at 08:40:48PM +0530, Ramesh Adhikari wrote:
> The roundup() and rounddown() macros return the rounded value but
> do not modify the input in place. In _badblocks_set(), _badblocks_clear(),
> and badblocks_check(), the return values were being discarded, causing
> s and target/next to remain unrounded. This resulted in sectors
> being calculated from unrounded values, which could lead to sectors
> being way too large (or zero), causing infinite loops in the
> re_insert/re_clear/re_check loops.
> 
> Additionally, add integer overflow checks (s > ULLONG_MAX - sectors)
> before the s + sectors calculation in all three functions to prevent
> overflow-related issues. Also add early return when sectors becomes
> zero after rounding in badblocks_check().
> 
> Root cause: When s and sectors have specific values (e.g., from
> syzkaller fuzzing via nvdimm ioctl), the unrounded values cause
> sectors to be incorrectly calculated. In _badblocks_clear(), this
> could result in needing 2^46 iterations to process 2^55 sectors,
> triggering RCU stall warnings and effectively hanging the kernel.
> 
> Fix by properly capturing the return values from roundup() and
> rounddown(), adding overflow checks before sector arithmetic, and
> handling the zero-sectors case in badblocks_check().
> 
> Signed-off-by: Ramesh Adhikari <adhikari.resume@gmail.com>
> ---
>  block/badblocks.c | 43 ++++++++++++++++++++++++++++++++++---------
>  1 file changed, 34 insertions(+), 9 deletions(-)
> 
> diff --git a/block/badblocks.c b/block/badblocks.c
> index ece64e76fe8..a5ffae65a05 100644
> --- a/block/badblocks.c
> +++ b/block/badblocks.c
> @@ -855,13 +855,21 @@ static bool _badblocks_set(struct badblocks *bb, sector_t s, sector_t sectors,
>  
>  	if (bb->shift) {
>  		/* round the start down, and the end up */
> +		if (s > ULLONG_MAX - sectors)
> +			return false;
>  		sector_t next = s + sectors;
>  
> -		rounddown(s, 1 << bb->shift);
> -		roundup(next, 1 << bb->shift);
> -		sectors = next - s;
> +		s = rounddown(s, 1 << bb->shift);
> +		next = roundup(next, 1 << bb->shift);
> +		if (next < s)
> +			sectors = 0;
> +		else
> +			sectors = next - s;
>  	}
>  
> +	if (sectors == 0)
> +		return false;
> +
>  	write_seqlock_irqsave(&bb->lock, flags);
>  
>  	bad.ack = acknowledged;
> @@ -1070,12 +1078,20 @@ static bool _badblocks_clear(struct badblocks *bb, sector_t s, sector_t sectors)
>  		 * However it is better the think a block is bad when it
>  		 * isn't than to think a block is not bad when it is.
>  		 */
> +		if (s > ULLONG_MAX - sectors)
> +			return false;
>  		target = s + sectors;
> -		roundup(s, 1 << bb->shift);
> -		rounddown(target, 1 << bb->shift);
> -		sectors = target - s;
> +		s = roundup(s, 1 << bb->shift);
> +		target = rounddown(target, 1 << bb->shift);
> +		if (target < s)
> +			sectors = 0;
> +		else
> +			sectors = target - s;
>  	}
>  
> +	if (sectors == 0)
> +		return false;
> +
>  	write_seqlock_irq(&bb->lock);
>  
>  	bad.ack = true;
> @@ -1305,11 +1321,20 @@ int badblocks_check(struct badblocks *bb, sector_t s, sector_t sectors,
>  
>  	if (bb->shift > 0) {
>  		/* round the start down, and the end up */
> +		if (s > ULLONG_MAX - sectors) {
> +			return -EINVAL;
> +		}
>  		sector_t target = s + sectors;
>  
> -		rounddown(s, 1 << bb->shift);
> -		roundup(target, 1 << bb->shift);
> -		sectors = target - s;
> +		s = rounddown(s, 1 << bb->shift);
> +		target = roundup(target, 1 << bb->shift);
> +		if (target < s)
> +			sectors = 0;
> +		else
> +			sectors = target - s;
> +
> +		if (sectors == 0)
> +			return 0;
>  	}
>  
>  retry:
> -- 
> 2.43.0
> 

Hi,

This is the friendly patch-bot of Greg Kroah-Hartman.  You have sent him
a patch that has triggered this response.  He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created.  Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.

You are receiving this message because of the following common error(s)
as indicated below:

- This looks like a new version of a previously submitted patch, but you
  did not list below the --- line any changes from the previous version.
  Please read the section entitled "The canonical patch format" in the
  kernel file, Documentation/process/submitting-patches.rst for what
  needs to be done here to properly describe this.

If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.

thanks,

greg k-h's patch email bot

^ permalink raw reply

* [PATCH] badblocks: fix infinite loop due to incorrect rounding and overflow
From: Ramesh Adhikari @ 2026-04-27 15:10 UTC (permalink / raw)
  To: axboe, gregkh; +Cc: linux-block, Ramesh Adhikari

The roundup() and rounddown() macros return the rounded value but
do not modify the input in place. In _badblocks_set(), _badblocks_clear(),
and badblocks_check(), the return values were being discarded, causing
s and target/next to remain unrounded. This resulted in sectors
being calculated from unrounded values, which could lead to sectors
being way too large (or zero), causing infinite loops in the
re_insert/re_clear/re_check loops.

Additionally, add integer overflow checks (s > ULLONG_MAX - sectors)
before the s + sectors calculation in all three functions to prevent
overflow-related issues. Also add early return when sectors becomes
zero after rounding in badblocks_check().

Root cause: When s and sectors have specific values (e.g., from
syzkaller fuzzing via nvdimm ioctl), the unrounded values cause
sectors to be incorrectly calculated. In _badblocks_clear(), this
could result in needing 2^46 iterations to process 2^55 sectors,
triggering RCU stall warnings and effectively hanging the kernel.

Fix by properly capturing the return values from roundup() and
rounddown(), adding overflow checks before sector arithmetic, and
handling the zero-sectors case in badblocks_check().

Signed-off-by: Ramesh Adhikari <adhikari.resume@gmail.com>
---
 block/badblocks.c | 43 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/block/badblocks.c b/block/badblocks.c
index ece64e76fe8..a5ffae65a05 100644
--- a/block/badblocks.c
+++ b/block/badblocks.c
@@ -855,13 +855,21 @@ static bool _badblocks_set(struct badblocks *bb, sector_t s, sector_t sectors,
 
 	if (bb->shift) {
 		/* round the start down, and the end up */
+		if (s > ULLONG_MAX - sectors)
+			return false;
 		sector_t next = s + sectors;
 
-		rounddown(s, 1 << bb->shift);
-		roundup(next, 1 << bb->shift);
-		sectors = next - s;
+		s = rounddown(s, 1 << bb->shift);
+		next = roundup(next, 1 << bb->shift);
+		if (next < s)
+			sectors = 0;
+		else
+			sectors = next - s;
 	}
 
+	if (sectors == 0)
+		return false;
+
 	write_seqlock_irqsave(&bb->lock, flags);
 
 	bad.ack = acknowledged;
@@ -1070,12 +1078,20 @@ static bool _badblocks_clear(struct badblocks *bb, sector_t s, sector_t sectors)
 		 * However it is better the think a block is bad when it
 		 * isn't than to think a block is not bad when it is.
 		 */
+		if (s > ULLONG_MAX - sectors)
+			return false;
 		target = s + sectors;
-		roundup(s, 1 << bb->shift);
-		rounddown(target, 1 << bb->shift);
-		sectors = target - s;
+		s = roundup(s, 1 << bb->shift);
+		target = rounddown(target, 1 << bb->shift);
+		if (target < s)
+			sectors = 0;
+		else
+			sectors = target - s;
 	}
 
+	if (sectors == 0)
+		return false;
+
 	write_seqlock_irq(&bb->lock);
 
 	bad.ack = true;
@@ -1305,11 +1321,20 @@ int badblocks_check(struct badblocks *bb, sector_t s, sector_t sectors,
 
 	if (bb->shift > 0) {
 		/* round the start down, and the end up */
+		if (s > ULLONG_MAX - sectors) {
+			return -EINVAL;
+		}
 		sector_t target = s + sectors;
 
-		rounddown(s, 1 << bb->shift);
-		roundup(target, 1 << bb->shift);
-		sectors = target - s;
+		s = rounddown(s, 1 << bb->shift);
+		target = roundup(target, 1 << bb->shift);
+		if (target < s)
+			sectors = 0;
+		else
+			sectors = target - s;
+
+		if (sectors == 0)
+			return 0;
 	}
 
 retry:
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH blktests] nvme/068: add a test for multipath delayed removal
From: John Garry @ 2026-04-27 12:51 UTC (permalink / raw)
  To: Daniel Wagner; +Cc: shinichiro.kawasaki, linux-block, linux-nvme, nilay
In-Reply-To: <655129a4-de84-4c10-9e55-f75143684b7c@flourine.local>

On 27/04/2026 13:17, Daniel Wagner wrote:
> On Wed, Apr 15, 2026 at 10:41:11AM +0000, John Garry wrote:
>> +	# Part a: Prove that writes fail when no path returns. Any reads or
>> +	#	  writes are queued during the delayed removal period. If no
>> +	#	  paths return before the timer expires, then those IOs should
>> +	#	  fail.
>> +	#	  During the delayed removal period, ensure that the module
>> +	#	  refcnt is incremented, to prove that we cannot remove the
>> +	#	  driver during this period.
>> +	nvmedev=$(_find_nvme_dev "${def_subsysnqn}")
>> +	ns=$(_find_nvme_ns "${def_subsys_uuid}")
>> +	refcnt=$(_module_use_count nvme_core)
>> +	echo 10 > "/sys/block/"$ns"/delayed_removal_secs"
>> +	refcnt_orig=$(_module_use_count nvme_core)
>> +	_nvme_disconnect_ctrl "${nvmedev}"
>> +	sleep 1
> Is there a better alternative to sleep for these tests? I'm worried
> about reliability and the extra runtime added by idling.

I suppose that I could check sysfs to ensure that the controller has 
disappeared (instead of the sleep) for the disconnect. And similar idea 
for reconnect.

However some of what we want to test is still based on timing - for 
example, if we set delayed removal timeout to X seconds, we want to 
ensure that the gendisk is gone after X seconds. So we need the X second 
delay. Currently X is 10, but can shorten that.

sound ok?




^ permalink raw reply

* Re: [PATCH v12 00/13] blk: honor isolcpus configuration
From: Florian Bezdeka @ 2026-04-27 10:55 UTC (permalink / raw)
  To: Aaron Tomlin, axboe, kbusch, hch, sagi, mst
  Cc: aacraid, James.Bottomley, martin.petersen, liyihang9,
	kashyap.desai, sumit.saxena, shivasharan.srikanteshwara,
	chandrakanth.patil, sathya.prakash, sreekanth.reddy,
	suganath-prabu.subramani, ranjan.kumar, jinpu.wang, tglx, mingo,
	peterz, juri.lelli, vincent.guittot, akpm, maz, ruanjinjie,
	bigeasy, yphbchou0911, wagi, frederic, longman, chenridong, hare,
	kch, ming.lei, tom.leiming, steve, sean, chjohnst, neelx, mproche,
	nick.lange, marco.crivellari, linux-block, linux-kernel,
	virtualization, linux-nvme, linux-scsi, megaraidlinux.pdl,
	mpi3mr-linuxdrv.pdl, MPT-FusionLinux.pdl
In-Reply-To: <20260422185215.100929-1-atomlin@atomlin.com>

Hi all,

On Wed, 2026-04-22 at 14:52 -0400, Aaron Tomlin wrote:
> Hi,
> 
> I have decided to drive this series forward on behalf of Daniel Wagner, the
> original author. The series has been rebased on v7.0-12635-g6596a02b2078.
> 
> Building upon prior iterations, this series introduces critical
> architectural refinements to the mapping and affinity spreading algorithms
> to guarantee thread safety and resilience against concurrent CPU-hotplug
> operations. Previously, the block layer relied on a shared global static
> mask (i.e., blk_hk_online_mask), which proved vulnerable to race conditions
> during rapid hotplug events. This vulnerability was highlighted by the
> kernel test robot, which encountered a NULL pointer dereference during
> rcutorture (cpuhotplug) stress testing due to concurrent mask modification.
> 
> To resolve this, the architecture has been fundamentally hardened. The
> global static state has been eradicated. Instead, the IRQ affinity core now
> employs a newly introduced irq_spread_hk_filter(), which safely intersects
> the natively calculated affinity mask with the HK_TYPE_IO_QUEUE mask.
> Crucially, this is achieved using a local, hotplug-safe snapshot via
> data_race(cpu_online_mask). This approach circumvents the hotplug lock
> deadlocks previously identified by Thomas Gleixner, while explicitly
> avoiding CONFIG_CPUMASK_OFFSTACK stack bloat hazards on high-core-count
> systems. A robust fallback mechanism guarantees that should an interrupt
> vector be assigned exclusively to isolated cores, it is safely re-routed to
> the system's online housekeeping CPUs.
> 
> Following rigorous testing of multiple queue maps (such as NVMe poll
> queues) alongside isolated CPUs, the tenth iteration resolved a critical
> page fault regression. The multi-queue mapping logic has been corrected to
> strictly maintain absolute hardware queue indices, ensuring faultless queue
> initialisation and preventing out-of-bounds memory access.
> 
> Furthermore, following feedback from Ming Lei, the administrative
> documentation for isolcpus=io_queue has undergone a comprehensive overhaul
> to reflect this architectural reality. Previous iterations lacked the
> required technical precision regarding subsystem impact. The expanded
> kernel-parameters.txt now explicitly details that this parameter applies
> strictly to managed IRQs. It thoroughly documents how the block layer
> intercepts multiqueue allocation to match the housekeeping mask, actively
> preventing MSI-X vector exhaustion on massive topologies and forcing queue
> sharing. Most importantly, it cements the structural guarantee: while an
> application on an isolated CPU may freely submit I/O, the hardware
> completion interrupt is strictly and safely offloaded to a housekeeping
> core.
> 
> Please let me know your thoughts.

This topic reminds me of a discussion started by Tobias [1] some time
ago about IRQ spreading of network drivers. The problem was (and still
is) that network drivers ignore any CPU isolation when spreading out
device IRQs.

In general we have two different CPU isolation mechanisms:
  - The static one, via isolcpus= cmdline parameter
  - The dynamic one, via cgroups(v2) cpuset controller

This series is only taking the static "world" into account, right? Are
there any plans to honor the CPU isolations configured the dynamic way?

It has been a while since the last investigations on my end. Last time I
went through the code, the IRQ core was completely decoupled from the
dynamic configuration via cgroups. Are there any plans to fix that gap?

Best regards,
Florian

[1] https://lore.kernel.org/all/a0cad8314124ca98d7c6763e3e08d7192598cf92.camel@siemens.com/

^ permalink raw reply

* Re: [PATCH v2 2/3] dm-inlinecrypt: add target for inline block device encryption
From: Linlin Zhang @ 2026-04-27 12:20 UTC (permalink / raw)
  To: Benjamin Marzinski
  Cc: linux-block, ebiggers, mpatocka, gmazyland, linux-kernel,
	adrianvovk, dm-devel, quic_mdalam, israelr, hch, axboe
In-Reply-To: <ae65vfakVKg2aGcp@redhat.com>



On 4/27/2026 9:19 AM, Benjamin Marzinski wrote:
> On Fri, Apr 10, 2026 at 06:40:30AM -0700, Linlin Zhang wrote:
>> From: Eric Biggers <ebiggers@google.com>
>>
>> Add a new device-mapper target "dm-inlinecrypt" that is similar to
>> dm-crypt but uses the blk-crypto API instead of the regular crypto API.
>> This allows it to take advantage of inline encryption hardware such as
>> that commonly built into UFS host controllers.
>>
>> The table syntax matches dm-crypt's, but for now only a stripped-down
>> set of parameters is supported.  For example, for now AES-256-XTS is the
>> only supported cipher.
>>
>> dm-inlinecrypt is based on Android's dm-default-key with the
>> controversial passthrough support removed.  Note that due to the removal
>> of passthrough support, use of dm-inlinecrypt in combination with
>> fscrypt causes double encryption of file contents (similar to dm-crypt +
>> fscrypt), with the fscrypt layer not being able to use the inline
>> encryption hardware.  This makes dm-inlinecrypt unusable on systems such
>> as Android that use fscrypt and where a more optimized approach is
>> needed.  It is however suitable as a replacement for dm-crypt.
>>
>> dm-inlinecrypt supports both keyring key and hex key, the former avoids
>> the key to be exposed in dm-table message. Similar to dm-default-key in
>> Android, it will fallabck to the software block crypto once the inline
>> crypto hardware cannot support the expected cipher.
>>
>> Test:
>> dmsetup create inlinecrypt_logon --table "0 `blockdev --getsz $1` \
>> inlinecrypt aes-xts-plain64 :64:logon:fde:dminlinecrypt_test_key 0 $1 0"
>>
>> Signed-off-by: Eric Biggers <ebiggers@google.com>
>> Signed-off-by: Linlin Zhang <linlin.zhang@oss.qualcomm.com>
>> ---
>>  drivers/md/Kconfig          |  10 +
>>  drivers/md/Makefile         |   1 +
>>  drivers/md/dm-inlinecrypt.c | 559 ++++++++++++++++++++++++++++++++++++
>>  3 files changed, 570 insertions(+)
>>  create mode 100644 drivers/md/dm-inlinecrypt.c
>>
>> diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
>> index c58a9a8ea54e..aa541cc22ecc 100644
>> --- a/drivers/md/Kconfig
>> +++ b/drivers/md/Kconfig
>> @@ -313,6 +313,16 @@ config DM_CRYPT
>>  
>>  	  If unsure, say N.
>>  
>> +config DM_INLINECRYPT
>> +	tristate "Inline encryption target support"
>> +	depends on BLK_DEV_DM
>> +	depends on BLK_INLINE_ENCRYPTION
>> +	help
>> +	  This device-mapper target is similar to dm-crypt, but it uses the
>> +	  blk-crypto API instead of the regular crypto API. This allows it to
>> +	  take advantage of inline encryption hardware such as that commonly
>> +	  built into UFS host controllers.
>> +
>>  config DM_SNAPSHOT
>>         tristate "Snapshot target"
>>         depends on BLK_DEV_DM
>> diff --git a/drivers/md/Makefile b/drivers/md/Makefile
>> index c338cc6fbe2e..517d1f7d8288 100644
>> --- a/drivers/md/Makefile
>> +++ b/drivers/md/Makefile
>> @@ -55,6 +55,7 @@ obj-$(CONFIG_DM_UNSTRIPED)	+= dm-unstripe.o
>>  obj-$(CONFIG_DM_BUFIO)		+= dm-bufio.o
>>  obj-$(CONFIG_DM_BIO_PRISON)	+= dm-bio-prison.o
>>  obj-$(CONFIG_DM_CRYPT)		+= dm-crypt.o
>> +obj-$(CONFIG_DM_INLINECRYPT)	+= dm-inlinecrypt.o
>>  obj-$(CONFIG_DM_DELAY)		+= dm-delay.o
>>  obj-$(CONFIG_DM_DUST)		+= dm-dust.o
>>  obj-$(CONFIG_DM_FLAKEY)		+= dm-flakey.o
>> diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c
>> new file mode 100644
>> index 000000000000..b6e98fdf8af1
>> --- /dev/null
>> +++ b/drivers/md/dm-inlinecrypt.c
>> @@ -0,0 +1,559 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright 2024 Google LLC
>> + */
>> +
>> +#include <linux/blk-crypto.h>
>> +#include <linux/ctype.h>
>> +#include <linux/device-mapper.h>
>> +#include <linux/hex.h>
>> +#include <linux/module.h>
>> +#include <keys/user-type.h>
>> +
>> +#define DM_MSG_PREFIX	"inlinecrypt"
>> +
>> +static const struct dm_inlinecrypt_cipher {
>> +	const char *name;
>> +	enum blk_crypto_mode_num mode_num;
>> +} dm_inlinecrypt_ciphers[] = {
>> +	{
>> +		.name = "aes-xts-plain64",
>> +		.mode_num = BLK_ENCRYPTION_MODE_AES_256_XTS,
>> +	},
>> +};
>> +
>> +/**
>> + * struct inlinecrypt_ctx - private data of an inlinecrypt target
>> + * @dev: the underlying device
>> + * @start: starting sector of the range of @dev which this target actually maps.
>> + *	   For this purpose a "sector" is 512 bytes.
>> + * @cipher_string: the name of the encryption algorithm being used
>> + * @iv_offset: starting offset for IVs.  IVs are generated as if the target were
>> + *	       preceded by @iv_offset 512-byte sectors.
>> + * @sector_size: crypto sector size in bytes (usually 4096)
>> + * @sector_bits: log2(sector_size)
>> + * @key: the encryption key to use
>> + * @max_dun: the maximum DUN that may be used (computed from other params)
>> + */
>> +struct inlinecrypt_ctx {
>> +	struct dm_dev *dev;
>> +	sector_t start;
>> +	const char *cipher_string;
>> +	unsigned int key_size;
>> +	u64 iv_offset;
>> +	unsigned int sector_size;
>> +	unsigned int sector_bits;
>> +	struct blk_crypto_key key;
>> +	u64 max_dun;
>> +};
>> +
>> +static const struct dm_inlinecrypt_cipher *
>> +lookup_cipher(const char *cipher_string)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(dm_inlinecrypt_ciphers); i++) {
>> +		if (strcmp(cipher_string, dm_inlinecrypt_ciphers[i].name) == 0)
>> +			return &dm_inlinecrypt_ciphers[i];
>> +	}
>> +	return NULL;
>> +}
>> +
>> +static void inlinecrypt_dtr(struct dm_target *ti)
>> +{
>> +	struct inlinecrypt_ctx *ctx = ti->private;
>> +
>> +	if (ctx->dev) {
>> +		if (ctx->key.size)
>> +			blk_crypto_evict_key(ctx->dev->bdev, &ctx->key);
>> +		dm_put_device(ti, ctx->dev);
>> +	}
>> +	kfree_sensitive(ctx->cipher_string);
>> +	kfree_sensitive(ctx);
>> +}
>> +
>> +static bool contains_whitespace(const char *str)
>> +{
>> +	while (*str)
>> +		if (isspace(*str++))
>> +			return true;
>> +	return false;
>> +}
>> +
>> +static int set_key_user(struct key *key, char *bin_key,
>> +			const unsigned int bin_key_size)
>> +{
>> +	const struct user_key_payload *ukp;
>> +
>> +	ukp = user_key_payload_locked(key);
>> +	if (!ukp)
>> +		return -EKEYREVOKED;
>> +
>> +	if (bin_key_size != ukp->datalen)
>> +		return -EINVAL;
>> +
>> +	memcpy(bin_key, ukp->data, bin_key_size);
>> +
>> +	return 0;
>> +}
>> +
>> +static int inlinecrypt_get_keyring_key(const char *key_string, u8 *bin_key,
>> +					const unsigned int bin_key_size)
>> +{
>> +	char *key_desc;
>> +	int ret;
>> +	struct key_type *type;
>> +	struct key *key;
> 
> There's nothing forcing CONFIG_KEYS to be set when CONFIG_DM_INLINECRYPT
> is, and without it, struct key won't be defined and this won't compile.

Thanks for your review!

ACK. I'll add 'depends on' in kconfig file for CONFIG_DM_INLINECRYPT and
corresponding '#ifdef CONFIG_KEYS' in this code.

> 
>> +	int (*set_key)(struct key *key, char *bin_key,
>> +				   const unsigned int bin_key_size);
>> +
>> +	/*
>> +	 * Reject key_string with whitespace. dm core currently lacks code for
>> +	 * proper whitespace escaping in arguments on DM_TABLE_STATUS path.
>> +	 */
>> +	if (contains_whitespace(key_string)) {
>> +		DMERR("whitespace chars not allowed in key string");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* look for next ':' separating key_type from key_description */
>> +	key_desc = strchr(key_string, ':');
>> +	if (!key_desc || key_desc == key_string || !strlen(key_desc + 1))
>> +		return -EINVAL;
>> +
>> +	if (!strncmp(key_string, "logon:", key_desc - key_string + 1)) {
>> +		type = &key_type_logon;
>> +		set_key = set_key_user;
>> +	} else {
>> +		return -EINVAL;
>> +	}
>> +
>> +	key = request_key(type, key_desc + 1, NULL);
>> +	if (IS_ERR(key))
>> +		return PTR_ERR(key);
>> +
>> +	down_read(&key->sem);
>> +
>> +	ret = set_key(key, (char *)bin_key, bin_key_size);
>> +	if (ret < 0) {
> 
> This does the same commands regardless of whether it takes this branch.

ACK. Need remove 'if' code block here.

> 
>> +		up_read(&key->sem);
>> +		key_put(key);
>> +		return ret;
>> +	}
>> +
>> +	up_read(&key->sem);
>> +	key_put(key);
>> +
>> +	return ret;
>> +}
>> +
>> +static int inlinecrypt_get_key(const char *key_string,
>> +				u8 key[BLK_CRYPTO_MAX_ANY_KEY_SIZE],
>> +				const unsigned int key_size)
>> +{
>> +	int ret = 0;
>> +
>> +	/* ':' means the key is in kernel keyring, short-circuit normal key processing */
>> +	if (key_string[0] == ':') {
>> +		if (key_size > BLK_CRYPTO_MAX_ANY_KEY_SIZE) {
>> +			DMERR("Invalid keysize");
>> +			return -EINVAL;
>> +		}
>> +		/* key string should be :<logon|user>:<key_desc> */
>> +		ret = inlinecrypt_get_keyring_key(key_string + 1, key, key_size);
>> +		goto out;
>> +	}
>> +
>> +	if (key_size > 2 * BLK_CRYPTO_MAX_ANY_KEY_SIZE
> 
> get_key_size() returns the size of the binary key in this case, so
> shouldn't this check for "key_size > BLK_CRYPTO_MAX_ANY_KEY_SIZE", and
> it seems like the check if the key_size is odd would make more sense in
> get_key_size().

Thanks for your insight!

ACK. Move the check if the key_size is odd into get_key_size() and only remains
this check for "key_size > BLK_CRYPTO_MAX_ANY_KEY_SIZE" here in this case.

> 
>> +		|| key_size  % 2
>> +		|| !key_size) {
>> +		DMERR("Invalid keysize");
>> +		return -EINVAL;
>> +	}
>> +	if (hex2bin(key, key_string, key_size) != 0)
>> +		ret = -EINVAL;
>> +
>> +out:
>> +	return ret;
>> +}
>> +
>> +static int get_key_size(char **key_string)
>> +{
>> +	char *colon, dummy;
>> +	int ret;
>> +
>> +	if (*key_string[0] != ':')
>> +		return strlen(*key_string) >> 1;
>> +
>> +	/* look for next ':' in key string */
>> +	colon = strpbrk(*key_string + 1, ":");
>> +	if (!colon)
>> +		return -EINVAL;
>> +
>> +	if (sscanf(*key_string + 1, "%u%c", &ret, &dummy) != 2 || dummy != ':')
>> +		return -EINVAL;
>> +
>> +	/* remaining key string should be :<logon|user>:<key_desc> */
>> +	*key_string = colon;
>> +
>> +	return ret;
>> +}
>> +
>> +static int inlinecrypt_ctr_optional(struct dm_target *ti,
>> +				    unsigned int argc, char **argv)
>> +{
>> +	struct inlinecrypt_ctx *ctx = ti->private;
>> +	struct dm_arg_set as;
>> +	static const struct dm_arg _args[] = {
>> +		{0, 3, "Invalid number of feature args"},
>> +	};
>> +	unsigned int opt_params;
>> +	const char *opt_string;
>> +	bool iv_large_sectors = false;
>> +	char dummy;
>> +	int err;
>> +
>> +	as.argc = argc;
>> +	as.argv = argv;
>> +
>> +	err = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
>> +	if (err)
>> +		return err;
>> +
>> +	while (opt_params--) {
>> +		opt_string = dm_shift_arg(&as);
>> +		if (!opt_string) {
>> +			ti->error = "Not enough feature arguments";
>> +			return -EINVAL;
>> +		}
>> +		if (!strcmp(opt_string, "allow_discards")) {
>> +			ti->num_discard_bios = 1;
>> +		} else if (sscanf(opt_string, "sector_size:%u%c",
>> +				  &ctx->sector_size, &dummy) == 1) {
>> +			if (ctx->sector_size < SECTOR_SIZE ||
>> +			    ctx->sector_size > 4096 ||
>> +			    !is_power_of_2(ctx->sector_size)) {
>> +				ti->error = "Invalid sector_size";
>> +				return -EINVAL;
>> +			}
>> +		} else if (!strcmp(opt_string, "iv_large_sectors")) {
>> +			iv_large_sectors = true;
>> +		} else {
>> +			ti->error = "Invalid feature arguments";
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	/* dm-inlinecrypt doesn't implement iv_large_sectors=false. */
>> +	if (ctx->sector_size != SECTOR_SIZE && !iv_large_sectors) {
>> +		ti->error = "iv_large_sectors must be specified";
> 
> Since setting sector_size forces setting iv_large_sectors, does it
> really need to be a separate parameter? Can't it just be implied by
> setting a non-512 sector_size. Is this here to futureproof the table
> line?

Thanks for your question!
Although a non-512 sector_size effectively requires large-sector IV semantics
for dm-inlinecrypt, iv_large_sectors is intentionally kept as an explicit table
parameter.

iv_large_sectors affects IV generation and the on-disk encryption format.
Inferring it from sector_size would silently change encryption semantics and
could break compatibility with existing data. Requiring it to be explicitly
specified forces userspace to consciously opt into the correct IV behavior,
rather than having it implied or overridden internally.

> 
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Construct an inlinecrypt mapping:
>> + * <cipher> [<key>|:<key_size>:<logon>:<key_description>] <iv_offset> <dev_path> <start>
>> + *
>> + * This syntax matches dm-crypt's, but the set of supported functionality has
>> + * been stripped down.
>> + */
>> +static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
>> +{
>> +	struct inlinecrypt_ctx *ctx;
>> +	const struct dm_inlinecrypt_cipher *cipher;
>> +	u8 raw_key[BLK_CRYPTO_MAX_ANY_KEY_SIZE];
>> +	unsigned int dun_bytes;
>> +	unsigned long long tmpll;
>> +	char dummy;
>> +	int err;
>> +
>> +	if (argc < 5) {
>> +		ti->error = "Not enough arguments";
>> +		return -EINVAL;
>> +	}
>> +
>> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
>> +	if (!ctx) {
>> +		ti->error = "Out of memory";
>> +		return -ENOMEM;
>> +	}
>> +	ti->private = ctx;
>> +
>> +	/* <cipher> */
>> +	ctx->cipher_string = kstrdup(argv[0], GFP_KERNEL);
>> +	if (!ctx->cipher_string) {
>> +		ti->error = "Out of memory";
>> +		err = -ENOMEM;
>> +		goto bad;
>> +	}
>> +	cipher = lookup_cipher(ctx->cipher_string);
>> +	if (!cipher) {
>> +		ti->error = "Unsupported cipher";
>> +		err = -EINVAL;
>> +		goto bad;
>> +	}
>> +
>> +	/* <key> */
>> +	ctx->key_size = get_key_size(&argv[1]);
>> +	if (ctx->key_size < 0) {
>> +		ti->error = "Cannot parse key size";
>> +		return -EINVAL;
>> +	}
>> +	err = inlinecrypt_get_key(argv[1], raw_key, ctx->key_size);
>> +	if (err) {
>> +		ti->error = "Malformed key string";
>> +		goto bad;
>> +	}
>> +
>> +	/* <iv_offset> */
>> +	if (sscanf(argv[2], "%llu%c", &ctx->iv_offset, &dummy) != 1) {
>> +		ti->error = "Invalid iv_offset sector";
>> +		err = -EINVAL;
>> +		goto bad;
>> +	}
>> +
>> +	/* <dev_path> */
>> +	err = dm_get_device(ti, argv[3], dm_table_get_mode(ti->table),
>> +			    &ctx->dev);
>> +	if (err) {
>> +		ti->error = "Device lookup failed";
>> +		goto bad;
>> +	}
>> +
>> +	/* <start> */
>> +	if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1 ||
>> +	    tmpll != (sector_t)tmpll) {
>> +		ti->error = "Invalid start sector";
>> +		err = -EINVAL;
>> +		goto bad;
>> +	}
>> +	ctx->start = tmpll;
>> +
>> +	/* optional arguments */
>> +	ctx->sector_size = SECTOR_SIZE;
>> +	if (argc > 5) {
>> +		err = inlinecrypt_ctr_optional(ti, argc - 5, &argv[5]);
>> +		if (err)
>> +			goto bad;
>> +	}
>> +	ctx->sector_bits = ilog2(ctx->sector_size);
>> +	if (ti->len & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) {
>> +		ti->error = "Device size is not a multiple of sector_size";
>> +		err = -EINVAL;
>> +		goto bad;
>> +	}
>> +
>> +	ctx->max_dun = (ctx->iv_offset + ti->len - 1) >>
>> +		       (ctx->sector_bits - SECTOR_SHIFT);
>> +	dun_bytes = DIV_ROUND_UP(fls64(ctx->max_dun), 8);
>> +
>> +	err = blk_crypto_init_key(&ctx->key, raw_key, ctx->key_size,
>> +				  BLK_CRYPTO_KEY_TYPE_RAW,
>> +				  cipher->mode_num, dun_bytes,
>> +				  ctx->sector_size);
>> +	if (err) {
>> +		ti->error = "Error initializing blk-crypto key";
>> +		goto bad;
>> +	}
>> +
>> +	err = blk_crypto_start_using_key(ctx->dev->bdev, &ctx->key);
>> +	if (err) {
>> +		ti->error = "Error starting to use blk-crypto";
>> +		goto bad;
>> +	}
>> +
>> +	ti->num_flush_bios = 1;
>> +
>> +	err = 0;
>> +	goto out;
>> +
>> +bad:
>> +	inlinecrypt_dtr(ti);
>> +out:
>> +	memzero_explicit(raw_key, sizeof(raw_key));
>> +	return err;
>> +}
>> +
>> +static int inlinecrypt_map(struct dm_target *ti, struct bio *bio)
>> +{
>> +	const struct inlinecrypt_ctx *ctx = ti->private;
>> +	sector_t sector_in_target;
>> +	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = {};
>> +
>> +	bio_set_dev(bio, ctx->dev->bdev);
>> +
>> +	/*
>> +	 * If the bio is a device-level request which doesn't target a specific
>> +	 * sector, there's nothing more to do.
>> +	 */
>> +	if (bio_sectors(bio) == 0)
>> +		return DM_MAPIO_REMAPPED;
>> +
>> +	/*
>> +	 * The bio should never have an encryption context already, since
>> +	 * dm-inlinecrypt doesn't pass through any inline encryption
>> +	 * capabilities to the layer above it.
>> +	 */
>> +	if (WARN_ON_ONCE(bio_has_crypt_ctx(bio)))
>> +		return DM_MAPIO_KILL;
>> +
>> +	/* Map the bio's sector to the underlying device. (512-byte sectors) */
>> +	sector_in_target = dm_target_offset(ti, bio->bi_iter.bi_sector);
>> +	bio->bi_iter.bi_sector = ctx->start + sector_in_target;
>> +	/*
>> +	 * If the bio doesn't have any data (e.g. if it's a DISCARD request),
>> +	 * there's nothing more to do.
>> +	 */
>> +	if (!bio_has_data(bio))
>> +		return DM_MAPIO_REMAPPED;
>> +
>> +	/* Calculate the DUN and enforce data-unit (crypto sector) alignment. */
>> +	dun[0] = ctx->iv_offset + sector_in_target; /* 512-byte sectors */
>> +	if (dun[0] & ((ctx->sector_size >> SECTOR_SHIFT) - 1))
>> +		return DM_MAPIO_KILL;
> 
> If ctx->iv_offset is not a multiple of ctx->sector_size, this will
> always fail. ctx->iv_offset should probably get validated in
> inlinecrypt_ctr()

ACK

Yes, this assumes iv_offset is aligned to sector_size when large crypto
sectors are used. That’s a requirement of dm-inlinecrypt semantics, and
adding an explicit check in inlinecrypt_ctr() would make this fail earlier
and more clearly.

> 
> -Ben
> 
>> +	dun[0] >>= ctx->sector_bits - SECTOR_SHIFT; /* crypto sectors */
>> +
>> +	/*
>> +	 * This check isn't necessary as we should have calculated max_dun
>> +	 * correctly, but be safe.
>> +	 */
>> +	if (WARN_ON_ONCE(dun[0] > ctx->max_dun))
>> +		return DM_MAPIO_KILL;
>> +
>> +	bio_crypt_set_ctx(bio, &ctx->key, dun, GFP_NOIO);
>> +
>> +	/*
>> +	 * Since we've added an encryption context to the bio and
>> +	 * blk-crypto-fallback may be needed to process it, it's necessary to
>> +	 * use the fallback-aware bio submission code rather than
>> +	 * unconditionally returning DM_MAPIO_REMAPPED.
>> +	 *
>> +	 * To get the correct accounting for a dm target in the case where
>> +	 * __blk_crypto_submit_bio() doesn't take ownership of the bio (returns
>> +	 * true), call __blk_crypto_submit_bio() directly and return
>> +	 * DM_MAPIO_REMAPPED in that case, rather than relying on
>> +	 * blk_crypto_submit_bio() which calls submit_bio() in that case.
>> +	 */
>> +	if (__blk_crypto_submit_bio(bio))
>> +		return DM_MAPIO_REMAPPED;
>> +	return DM_MAPIO_SUBMITTED;
>> +}
>> +
>> +static void inlinecrypt_status(struct dm_target *ti, status_type_t type,
>> +			       unsigned int status_flags, char *result,
>> +			       unsigned int maxlen)
>> +{
>> +	const struct inlinecrypt_ctx *ctx = ti->private;
>> +	unsigned int sz = 0;
>> +	int num_feature_args = 0;
>> +
>> +	switch (type) {
>> +	case STATUSTYPE_INFO:
>> +	case STATUSTYPE_IMA:
>> +		result[0] = '\0';
>> +		break;
>> +
>> +	case STATUSTYPE_TABLE:
>> +		/*
>> +		 * Warning: like dm-crypt, dm-inlinecrypt includes the key in
>> +		 * the returned table.  Userspace is responsible for redacting
>> +		 * the key when needed.
>> +		 */
>> +		DMEMIT("%s %*phN %llu %s %llu", ctx->cipher_string,
>> +		       ctx->key.size, ctx->key.bytes, ctx->iv_offset,
>> +		       ctx->dev->name, ctx->start);
>> +		num_feature_args += !!ti->num_discard_bios;
>> +		if (ctx->sector_size != SECTOR_SIZE)
>> +			num_feature_args += 2;
>> +		if (num_feature_args != 0) {
>> +			DMEMIT(" %d", num_feature_args);
>> +			if (ti->num_discard_bios)
>> +				DMEMIT(" allow_discards");
>> +			if (ctx->sector_size != SECTOR_SIZE) {
>> +				DMEMIT(" sector_size:%u", ctx->sector_size);
>> +				DMEMIT(" iv_large_sectors");
>> +			}
>> +		}
>> +		break;
>> +	}
>> +}
>> +
>> +static int inlinecrypt_prepare_ioctl(struct dm_target *ti,
>> +				     struct block_device **bdev, unsigned int cmd,
>> +				     unsigned long arg, bool *forward)
>> +{
>> +	const struct inlinecrypt_ctx *ctx = ti->private;
>> +	const struct dm_dev *dev = ctx->dev;
>> +
>> +	*bdev = dev->bdev;
>> +
>> +	/* Only pass ioctls through if the device sizes match exactly. */
>> +	return ctx->start != 0 || ti->len != bdev_nr_sectors(dev->bdev);
>> +}
>> +
>> +static int inlinecrypt_iterate_devices(struct dm_target *ti,
>> +				       iterate_devices_callout_fn fn,
>> +				       void *data)
>> +{
>> +	const struct inlinecrypt_ctx *ctx = ti->private;
>> +
>> +	return fn(ti, ctx->dev, ctx->start, ti->len, data);
>> +}
>> +
>> +#ifdef CONFIG_BLK_DEV_ZONED
>> +static int inlinecrypt_report_zones(struct dm_target *ti,
>> +				    struct dm_report_zones_args *args,
>> +				    unsigned int nr_zones)
>> +{
>> +	const struct inlinecrypt_ctx *ctx = ti->private;
>> +
>> +	return dm_report_zones(ctx->dev->bdev, ctx->start,
>> +			ctx->start + dm_target_offset(ti, args->next_sector),
>> +			args, nr_zones);
>> +}
>> +#else
>> +#define inlinecrypt_report_zones NULL
>> +#endif
>> +
>> +static void inlinecrypt_io_hints(struct dm_target *ti,
>> +				 struct queue_limits *limits)
>> +{
>> +	const struct inlinecrypt_ctx *ctx = ti->private;
>> +	const unsigned int sector_size = ctx->sector_size;
>> +
>> +	limits->logical_block_size =
>> +		max_t(unsigned int, limits->logical_block_size, sector_size);
>> +	limits->physical_block_size =
>> +		max_t(unsigned int, limits->physical_block_size, sector_size);
>> +	limits->io_min = max_t(unsigned int, limits->io_min, sector_size);
>> +	limits->dma_alignment = limits->logical_block_size - 1;
>> +}
>> +
>> +static struct target_type inlinecrypt_target = {
>> +	.name			= "inlinecrypt",
>> +	.version		= {1, 0, 0},
>> +	/*
>> +	 * Do not set DM_TARGET_PASSES_CRYPTO, since dm-inlinecrypt consumes the
>> +	 * crypto capability itself.
>> +	 */
>> +	.features		= DM_TARGET_ZONED_HM,
>> +	.module			= THIS_MODULE,
>> +	.ctr			= inlinecrypt_ctr,
>> +	.dtr			= inlinecrypt_dtr,
>> +	.map			= inlinecrypt_map,
>> +	.status			= inlinecrypt_status,
>> +	.prepare_ioctl		= inlinecrypt_prepare_ioctl,
>> +	.iterate_devices	= inlinecrypt_iterate_devices,
>> +	.report_zones		= inlinecrypt_report_zones,
>> +	.io_hints		= inlinecrypt_io_hints,
>> +};
>> +
>> +module_dm(inlinecrypt);
>> +
>> +MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
>> +MODULE_AUTHOR("Linlin Zhang <linlin.zhang@oss.qualcomm.com>");
>> +MODULE_DESCRIPTION(DM_NAME " target for inline encryption");
>> +MODULE_LICENSE("GPL");
>> -- 
>> 2.34.1
>>
> 


^ permalink raw reply

* Re: [PATCH blktests] nvme/068: add a test for multipath delayed removal
From: Daniel Wagner @ 2026-04-27 12:17 UTC (permalink / raw)
  To: John Garry; +Cc: shinichiro.kawasaki, linux-block, linux-nvme, nilay
In-Reply-To: <20260415104111.1439459-1-john.g.garry@oracle.com>

On Wed, Apr 15, 2026 at 10:41:11AM +0000, John Garry wrote:
> +	# Part a: Prove that writes fail when no path returns. Any reads or
> +	#	  writes are queued during the delayed removal period. If no
> +	#	  paths return before the timer expires, then those IOs should
> +	#	  fail.
> +	#	  During the delayed removal period, ensure that the module
> +	#	  refcnt is incremented, to prove that we cannot remove the
> +	#	  driver during this period.
> +	nvmedev=$(_find_nvme_dev "${def_subsysnqn}")
> +	ns=$(_find_nvme_ns "${def_subsys_uuid}")
> +	refcnt=$(_module_use_count nvme_core)
> +	echo 10 > "/sys/block/"$ns"/delayed_removal_secs"
> +	refcnt_orig=$(_module_use_count nvme_core)
> +	_nvme_disconnect_ctrl "${nvmedev}"
> +	sleep 1

Is there a better alternative to sleep for these tests? I'm worried
about reliability and the extra runtime added by idling.

^ permalink raw reply

* Re: [LSF/MM/BPF ATTEND][LSF/MM/BPF TOPIC] : blktests: status, expansion plan for the storage stack test framework
From: Shin'ichiro Kawasaki @ 2026-04-27 11:50 UTC (permalink / raw)
  To: Nilay Shroff
  Cc: Daniel Wagner, Chaitanya Kulkarni, linux-block@vger.kernel.org,
	linux-scsi@vger.kernel.org, linux-nvme@lists.infradead.org,
	lsf-pc@lists.linux-foundation.org, Bart Van Assche,
	Hannes Reinecke, hch, Jens Axboe, sagi@grimberg.me, tytso@mit.edu,
	Johannes Thumshirn, Christian Brauner, Martin K. Petersen,
	linux-fsdevel@vger.kernel.org, Javier González,
	willy@infradead.org, Jan Kara, amir73il@gmail.com, vbabka@suse.cz,
	Damien Le Moal
In-Reply-To: <d6282aa7-4673-4bae-a0ff-fbd84f0a610f@linux.ibm.com>

On Apr 23, 2026 / 13:35, Nilay Shroff wrote:
> On 4/21/26 11:49 AM, Shin'ichiro Kawasaki wrote:
> > On Feb 16, 2026 / 00:08, Nilay Shroff wrote:
> > > On 2/13/26 4:53 PM, Shinichiro Kawasaki wrote:
[...]
> > > >   4. Long standing failures make test result reports dirty
> > > >      - I feel lockdep WARNs are tend to be left unfixed rather long period.
> > > >        How can we gather effort to fix them?
> > > 
> > > I agree regarding lockdep; recently we did see quite a few lockdep splats.
> > > That said, I believe the number has dropped significantly and only a small
> > > set remains. From what I can tell, most of the outstanding lockdep issues
> > > are related to fs-reclaim paths recursing into the block layer while the
> > > queue is frozen. We should be able to resolve most of these soon, or at
> > > least before the conference. If anything is still outstanding after that,
> > > we can discuss it during the conference and work toward addressing it as
> > > quickly as possible.
> > 
> > Taking this chance, I'd like to express my appreciation for the effort to
> > resolve the lockdep issues. It is great that a number of lockdeps are already
> > fixed. Said that, two lockdep issues are still observed with v7.0 kernel at
> > nvme/005 and nbd/002 [1]. I would like to gather attentions to the failures.
> > 
> > [1] https://lore.kernel.org/linux-block/ynmi72x5wt5ooljjafebhcarit3pvu6axkslqenikb2p5txe57@ldytqa2t4i2x/
> > 
> I think nvme/005 and nbd/002 failures shall be addressed with this
> patch: https://lore.kernel.org/all/20260413171628.6204-1-kch@nvidia.com/
> 
> It's currently applied to nvme-7.1 and not there yet to mainline kernel.

Ah, I missed that patch. Thanks a lot, Chaitanya!

Today, I applied the nvme fix patch on top of v7.1-rc1, and ran nvme/005 with
tcp transport. Unfortunately, I still observe the lockdep splat for
&q->elevator_lock, &q->q_usage_counter(io) and set->srcu [*]. This time, the
call chain looks a bit different (cpu_hotplug_lock is involved?).

I also still observe the nbd/002 failure. The nvme fix patch does not affect
nbd, then I think it is expected that the nbd/002 failure is still there.


[*]

Apr 27 20:32:07 testnode1 unknown: run blktests nvme/005 at 2026-04-27 20:32:07
Apr 27 20:32:08 testnode1 kernel: loop0: detected capacity change from 0 to 2097152
Apr 27 20:32:08 testnode1 kernel: nvmet: adding nsid 1 to subsystem blktests-subsystem-1
Apr 27 20:32:08 testnode1 kernel: nvmet_tcp: enabling port 0 (127.0.0.1:4420)
Apr 27 20:32:08 testnode1 kernel: nvmet: Created nvm controller 1 for subsystem blktests-subsystem-1 for NQN nqn.2014-08.org.nvmexpress:uuid:0f01fb42-9f7f-4856-b0b3-51e60b8de349.
Apr 27 20:32:08 testnode1 kernel: nvme nvme5: creating 4 I/O queues.
Apr 27 20:32:08 testnode1 kernel: nvme nvme5: mapped 4/0/0 default/read/poll queues.
Apr 27 20:32:08 testnode1 kernel: nvme nvme5: new ctrl: NQN "blktests-subsystem-1", addr 127.0.0.1:4420, hostnqn: nqn.2014-08.org.nvmexpress:uuid:0f01fb42-9f7f-4856-b0b3-51e60b8de349
Apr 27 20:32:08 testnode1 kernel: nvmet: Created nvm controller 2 for subsystem blktests-subsystem-1 for NQN nqn.2014-08.org.nvmexpress:uuid:0f01fb42-9f7f-4856-b0b3-51e60b8de349.
Apr 27 20:32:08 testnode1 kernel: nvme nvme5: creating 4 I/O queues.
Apr 27 20:32:08 testnode1 kernel: nvme nvme5: mapped 4/0/0 default/read/poll queues.
Apr 27 20:32:08 testnode1 kernel: nvme nvme5: Removing ctrl: NQN "blktests-subsystem-1"
Apr 27 20:32:08 testnode1 kernel: 
Apr 27 20:32:08 testnode1 kernel: ======================================================
Apr 27 20:32:08 testnode1 kernel: WARNING: possible circular locking dependency detected
Apr 27 20:32:08 testnode1 kernel: 7.1.0-rc1+ #3 Not tainted
Apr 27 20:32:08 testnode1 kernel: ------------------------------------------------------
Apr 27 20:32:08 testnode1 kernel: nvme/1171 is trying to acquire lock:
Apr 27 20:32:08 testnode1 kernel: ffff888121e8bb98 (set->srcu){.+.+}-{0:0}, at: __synchronize_srcu+0x21/0x2b0
Apr 27 20:32:08 testnode1 kernel: 
                                  but task is already holding lock:
Apr 27 20:32:08 testnode1 kernel: ffff88812ab7bd68 (&q->elevator_lock){+.+.}-{4:4}, at: elevator_change+0x188/0x4f0
Apr 27 20:32:08 testnode1 kernel: 
                                  which lock already depends on the new lock.
Apr 27 20:32:08 testnode1 kernel: 
                                  the existing dependency chain (in reverse order) is:
Apr 27 20:32:08 testnode1 kernel: 
                                  -> #5 (&q->elevator_lock){+.+.}-{4:4}:
Apr 27 20:32:08 testnode1 kernel:        __mutex_lock+0x1ae/0x2600
Apr 27 20:32:08 testnode1 kernel:        elevator_change+0x188/0x4f0
Apr 27 20:32:08 testnode1 kernel:        elv_iosched_store+0x308/0x390
Apr 27 20:32:08 testnode1 kernel:        queue_attr_store+0x23b/0x360
Apr 27 20:32:08 testnode1 kernel:        kernfs_fop_write_iter+0x3d6/0x5e0
Apr 27 20:32:08 testnode1 kernel:        vfs_write+0x52c/0xf80
Apr 27 20:32:08 testnode1 kernel:        ksys_write+0xfb/0x200
Apr 27 20:32:08 testnode1 kernel:        do_syscall_64+0xdd/0x14c0
Apr 27 20:32:08 testnode1 kernel:        entry_SYSCALL_64_after_hwframe+0x76/0x7e
Apr 27 20:32:08 testnode1 kernel: 
                                  -> #4 (&q->q_usage_counter(io)){++++}-{0:0}:
Apr 27 20:32:08 testnode1 kernel:        blk_alloc_queue+0x5b3/0x730
Apr 27 20:32:08 testnode1 kernel:        blk_mq_alloc_queue+0x13f/0x250
Apr 27 20:32:08 testnode1 kernel:        scsi_alloc_sdev+0x84e/0xca0
Apr 27 20:32:08 testnode1 kernel:        scsi_probe_and_add_lun+0x63f/0xc30
Apr 27 20:32:08 testnode1 kernel:        __scsi_add_device+0x1be/0x1f0
Apr 27 20:32:08 testnode1 kernel:        ata_scsi_scan_host+0x139/0x3a0
Apr 27 20:32:08 testnode1 kernel:        async_run_entry_fn+0x93/0x550
Apr 27 20:32:08 testnode1 kernel:        process_one_work+0x8b4/0x1640
Apr 27 20:32:08 testnode1 kernel:        worker_thread+0x606/0xff0
Apr 27 20:32:08 testnode1 kernel:        kthread+0x368/0x460
Apr 27 20:32:08 testnode1 kernel:        ret_from_fork+0x653/0x9d0
Apr 27 20:32:08 testnode1 kernel:        ret_from_fork_asm+0x1a/0x30
Apr 27 20:32:08 testnode1 kernel: 
                                  -> #3 (fs_reclaim){+.+.}-{0:0}:
Apr 27 20:32:08 testnode1 kernel:        fs_reclaim_acquire+0xd5/0x120
Apr 27 20:32:08 testnode1 kernel:        __kmalloc_cache_node_noprof+0x51/0x740
Apr 27 20:32:08 testnode1 kernel:        create_worker+0xfb/0x710
Apr 27 20:32:08 testnode1 kernel:        workqueue_prepare_cpu+0x87/0xe0
Apr 27 20:32:08 testnode1 kernel:        cpuhp_invoke_callback+0x2a7/0x1230
Apr 27 20:32:08 testnode1 kernel:        __cpuhp_invoke_callback_range+0xbd/0x1f0
Apr 27 20:32:08 testnode1 kernel:        _cpu_up+0x2ec/0x700
Apr 27 20:32:08 testnode1 kernel:        cpu_up+0x111/0x190
Apr 27 20:32:08 testnode1 kernel:        cpuhp_bringup_mask+0xd3/0x110
Apr 27 20:32:08 testnode1 kernel:        bringup_nonboot_cpus+0x139/0x170
Apr 27 20:32:08 testnode1 kernel:        smp_init+0x27/0xe0
Apr 27 20:32:08 testnode1 kernel:        kernel_init_freeable+0x445/0x6f0
Apr 27 20:32:08 testnode1 kernel:        kernel_init+0x18/0x150
Apr 27 20:32:08 testnode1 kernel:        ret_from_fork+0x653/0x9d0
Apr 27 20:32:08 testnode1 kernel:        ret_from_fork_asm+0x1a/0x30
Apr 27 20:32:08 testnode1 kernel: 
                                  -> #2 (cpu_hotplug_lock){++++}-{0:0}:
Apr 27 20:32:08 testnode1 kernel:        cpus_read_lock+0x3c/0xe0
Apr 27 20:32:08 testnode1 kernel:        static_key_disable+0x12/0x30
Apr 27 20:32:08 testnode1 kernel:        __inet_hash_connect+0x10f7/0x1a50
Apr 27 20:32:08 testnode1 kernel:        tcp_v4_connect+0xcb0/0x18b0
Apr 27 20:32:08 testnode1 kernel:        __inet_stream_connect+0x349/0xf00
Apr 27 20:32:08 testnode1 kernel:        inet_stream_connect+0x55/0xb0
Apr 27 20:32:08 testnode1 kernel:        kernel_connect+0xdf/0x140
Apr 27 20:32:08 testnode1 kernel:        nvme_tcp_alloc_queue+0xa48/0x1b60 [nvme_tcp]
Apr 27 20:32:08 testnode1 kernel:        nvme_tcp_alloc_admin_queue+0xff/0x440 [nvme_tcp]
Apr 27 20:32:08 testnode1 kernel:        nvme_tcp_setup_ctrl+0x8a/0x830 [nvme_tcp]
Apr 27 20:32:08 testnode1 kernel:        nvme_tcp_create_ctrl+0x834/0xb90 [nvme_tcp]
Apr 27 20:32:08 testnode1 kernel:        nvmf_dev_write+0x3e3/0x800 [nvme_fabrics]
Apr 27 20:32:08 testnode1 kernel:        vfs_write+0x1cc/0xf80
Apr 27 20:32:08 testnode1 kernel:        ksys_write+0xfb/0x200
Apr 27 20:32:08 testnode1 kernel:        do_syscall_64+0xdd/0x14c0
Apr 27 20:32:08 testnode1 kernel:        entry_SYSCALL_64_after_hwframe+0x76/0x7e
Apr 27 20:32:08 testnode1 kernel: 
                                  -> #1 (sk_lock-AF_INET-NVME){+.+.}-{0:0}:
Apr 27 20:32:08 testnode1 kernel:        lock_sock_nested+0x32/0xf0
Apr 27 20:32:08 testnode1 kernel:        tcp_sendmsg+0x1c/0x50
Apr 27 20:32:08 testnode1 kernel:        sock_sendmsg+0x2bd/0x370
Apr 27 20:32:08 testnode1 kernel:        nvme_tcp_try_send_cmd_pdu+0x57f/0xbd0 [nvme_tcp]
Apr 27 20:32:08 testnode1 kernel:        nvme_tcp_try_send+0x1b3/0x9c0 [nvme_tcp]
Apr 27 20:32:08 testnode1 kernel:        nvme_tcp_queue_rq+0xf77/0x1970 [nvme_tcp]
Apr 27 20:32:08 testnode1 kernel:        blk_mq_dispatch_rq_list+0x39b/0x2340
Apr 27 20:32:08 testnode1 kernel:        __blk_mq_sched_dispatch_requests+0x1dd/0x1510
Apr 27 20:32:08 testnode1 kernel:        blk_mq_sched_dispatch_requests+0xa8/0x150
Apr 27 20:32:08 testnode1 kernel:        blk_mq_run_work_fn+0x127/0x2c0
Apr 27 20:32:08 testnode1 kernel:        process_one_work+0x8b4/0x1640
Apr 27 20:32:08 testnode1 kernel:        worker_thread+0x606/0xff0
Apr 27 20:32:08 testnode1 kernel:        kthread+0x368/0x460
Apr 27 20:32:08 testnode1 kernel:        ret_from_fork+0x653/0x9d0
Apr 27 20:32:08 testnode1 kernel:        ret_from_fork_asm+0x1a/0x30
Apr 27 20:32:08 testnode1 kernel: 
                                  -> #0 (set->srcu){.+.+}-{0:0}:
Apr 27 20:32:08 testnode1 kernel:        __lock_acquire+0x14a6/0x2230
Apr 27 20:32:08 testnode1 kernel:        lock_sync+0xbd/0x120
Apr 27 20:32:08 testnode1 kernel:        __synchronize_srcu+0xa1/0x2b0
Apr 27 20:32:08 testnode1 kernel:        elevator_switch+0x2a5/0x680
Apr 27 20:32:08 testnode1 kernel:        elevator_change+0x2d8/0x4f0
Apr 27 20:32:08 testnode1 kernel:        elevator_set_none+0x87/0xd0
Apr 27 20:32:08 testnode1 kernel:        blk_unregister_queue+0x13f/0x2b0
Apr 27 20:32:08 testnode1 kernel:        __del_gendisk+0x263/0x9e0
Apr 27 20:32:08 testnode1 kernel:        del_gendisk+0x102/0x190
Apr 27 20:32:08 testnode1 kernel:        nvme_ns_remove+0x32a/0x900 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:        nvme_remove_namespaces+0x263/0x3b0 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:        nvme_do_delete_ctrl+0xf5/0x160 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:        nvme_delete_ctrl_sync.cold+0x8/0xd [nvme_core]
Apr 27 20:32:08 testnode1 kernel:        nvme_sysfs_delete+0x96/0xc0 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:        kernfs_fop_write_iter+0x3d6/0x5e0
Apr 27 20:32:08 testnode1 kernel:        vfs_write+0x52c/0xf80
Apr 27 20:32:08 testnode1 kernel:        ksys_write+0xfb/0x200
Apr 27 20:32:08 testnode1 kernel:        do_syscall_64+0xdd/0x14c0
Apr 27 20:32:08 testnode1 kernel:        entry_SYSCALL_64_after_hwframe+0x76/0x7e
Apr 27 20:32:08 testnode1 kernel: 
                                  other info that might help us debug this:
Apr 27 20:32:08 testnode1 kernel: Chain exists of:
                                    set->srcu --> &q->q_usage_counter(io) --> &q->elevator_lock
Apr 27 20:32:08 testnode1 kernel:  Possible unsafe locking scenario:
Apr 27 20:32:08 testnode1 kernel:        CPU0                    CPU1
Apr 27 20:32:08 testnode1 kernel:        ----                    ----
Apr 27 20:32:08 testnode1 kernel:   lock(&q->elevator_lock);
Apr 27 20:32:08 testnode1 kernel:                                lock(&q->q_usage_counter(io));
Apr 27 20:32:08 testnode1 kernel:                                lock(&q->elevator_lock);
Apr 27 20:32:08 testnode1 kernel:   sync(set->srcu);
Apr 27 20:32:08 testnode1 kernel: 
                                   *** DEADLOCK ***
Apr 27 20:32:08 testnode1 kernel: 5 locks held by nvme/1171:
Apr 27 20:32:08 testnode1 kernel:  #0: ffff88810868e410 (sb_writers#4){.+.+}-{0:0}, at: ksys_write+0xfb/0x200
Apr 27 20:32:08 testnode1 kernel:  #1: ffff88814e03f080 (&of->mutex#2){+.+.}-{4:4}, at: kernfs_fop_write_iter+0x257/0x5e0
Apr 27 20:32:08 testnode1 kernel:  #2: ffff88814e3f84b8 (kn->active#140){++++}-{0:0}, at: sysfs_remove_file_self+0x61/0xb0
Apr 27 20:32:08 testnode1 kernel:  #3: ffff8881073281c8 (&set->update_nr_hwq_lock){++++}-{4:4}, at: del_gendisk+0xfa/0x190
Apr 27 20:32:08 testnode1 kernel:  #4: ffff88812ab7bd68 (&q->elevator_lock){+.+.}-{4:4}, at: elevator_change+0x188/0x4f0
Apr 27 20:32:08 testnode1 kernel: 
                                  stack backtrace:
Apr 27 20:32:08 testnode1 kernel: CPU: 3 UID: 0 PID: 1171 Comm: nvme Not tainted 7.1.0-rc1+ #3 PREEMPT(full) 
Apr 27 20:32:08 testnode1 kernel: Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-9.fc43 06/10/2025
Apr 27 20:32:08 testnode1 kernel: Call Trace:
Apr 27 20:32:08 testnode1 kernel:  <TASK>
Apr 27 20:32:08 testnode1 kernel:  dump_stack_lvl+0x6a/0x90
Apr 27 20:32:08 testnode1 kernel:  print_circular_bug.cold+0x185/0x1d0
Apr 27 20:32:08 testnode1 kernel:  check_noncircular+0x148/0x170
Apr 27 20:32:08 testnode1 kernel:  __lock_acquire+0x14a6/0x2230
Apr 27 20:32:08 testnode1 kernel:  lock_sync+0xbd/0x120
Apr 27 20:32:08 testnode1 kernel:  ? __synchronize_srcu+0x21/0x2b0
Apr 27 20:32:08 testnode1 kernel:  ? __synchronize_srcu+0x21/0x2b0
Apr 27 20:32:08 testnode1 kernel:  __synchronize_srcu+0xa1/0x2b0
Apr 27 20:32:08 testnode1 kernel:  ? __pfx___synchronize_srcu+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? kvm_clock_get_cycles+0x14/0x30
Apr 27 20:32:08 testnode1 kernel:  ? ktime_get_mono_fast_ns+0x193/0x490
Apr 27 20:32:08 testnode1 kernel:  ? lockdep_hardirqs_on+0x88/0x130
Apr 27 20:32:08 testnode1 kernel:  ? _raw_spin_unlock_irqrestore+0x4c/0x60
Apr 27 20:32:08 testnode1 kernel:  elevator_switch+0x2a5/0x680
Apr 27 20:32:08 testnode1 kernel:  elevator_change+0x2d8/0x4f0
Apr 27 20:32:08 testnode1 kernel:  elevator_set_none+0x87/0xd0
Apr 27 20:32:08 testnode1 kernel:  ? __pfx_elevator_set_none+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? kobject_put+0x5a/0x4e0
Apr 27 20:32:08 testnode1 kernel:  blk_unregister_queue+0x13f/0x2b0
Apr 27 20:32:08 testnode1 kernel:  __del_gendisk+0x263/0x9e0
Apr 27 20:32:08 testnode1 kernel:  ? down_read+0x13b/0x480
Apr 27 20:32:08 testnode1 kernel:  ? __pfx___del_gendisk+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? __pfx_down_read+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? up_write+0x294/0x510
Apr 27 20:32:08 testnode1 kernel:  del_gendisk+0x102/0x190
Apr 27 20:32:08 testnode1 kernel:  nvme_ns_remove+0x32a/0x900 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:  nvme_remove_namespaces+0x263/0x3b0 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:  ? __pfx_nvme_remove_namespaces+0x10/0x10 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:  nvme_do_delete_ctrl+0xf5/0x160 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:  nvme_delete_ctrl_sync.cold+0x8/0xd [nvme_core]
Apr 27 20:32:08 testnode1 kernel:  nvme_sysfs_delete+0x96/0xc0 [nvme_core]
Apr 27 20:32:08 testnode1 kernel:  ? __pfx_sysfs_kf_write+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  kernfs_fop_write_iter+0x3d6/0x5e0
Apr 27 20:32:08 testnode1 kernel:  ? __pfx_kernfs_fop_write_iter+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  vfs_write+0x52c/0xf80
Apr 27 20:32:08 testnode1 kernel:  ? __pfx_vfs_write+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? kasan_save_free_info+0x37/0x70
Apr 27 20:32:08 testnode1 kernel:  ? __kasan_slab_free+0x67/0x80
Apr 27 20:32:08 testnode1 kernel:  ? kmem_cache_free+0x14c/0x670
Apr 27 20:32:08 testnode1 kernel:  ? do_sys_openat2+0xfd/0x170
Apr 27 20:32:08 testnode1 kernel:  ? __x64_sys_openat+0x10a/0x210
Apr 27 20:32:08 testnode1 kernel:  ksys_write+0xfb/0x200
Apr 27 20:32:08 testnode1 kernel:  ? __pfx_ksys_write+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  do_syscall_64+0xdd/0x14c0
Apr 27 20:32:08 testnode1 kernel:  ? kasan_quarantine_put+0xff/0x220
Apr 27 20:32:08 testnode1 kernel:  ? lockdep_hardirqs_on+0x88/0x130
Apr 27 20:32:08 testnode1 kernel:  ? kasan_quarantine_put+0xff/0x220
Apr 27 20:32:08 testnode1 kernel:  ? kasan_quarantine_put+0xff/0x220
Apr 27 20:32:08 testnode1 kernel:  ? do_sys_openat2+0xfd/0x170
Apr 27 20:32:08 testnode1 kernel:  ? kmem_cache_free+0x14c/0x670
Apr 27 20:32:08 testnode1 kernel:  ? do_sys_openat2+0xfd/0x170
Apr 27 20:32:08 testnode1 kernel:  ? __pfx_do_sys_openat2+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? kmem_cache_free+0x14c/0x670
Apr 27 20:32:08 testnode1 kernel:  ? __x64_sys_openat+0x10a/0x210
Apr 27 20:32:08 testnode1 kernel:  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
Apr 27 20:32:08 testnode1 kernel:  ? __pfx___x64_sys_openat+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? rcu_is_watching+0x11/0xb0
Apr 27 20:32:08 testnode1 kernel:  ? do_syscall_64+0x1ea/0x14c0
Apr 27 20:32:08 testnode1 kernel:  ? lockdep_hardirqs_on+0x88/0x130
Apr 27 20:32:08 testnode1 kernel:  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
Apr 27 20:32:08 testnode1 kernel:  ? do_syscall_64+0x208/0x14c0
Apr 27 20:32:08 testnode1 kernel:  ? __pfx___x64_sys_openat+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? __pfx___x64_sys_openat+0x10/0x10
Apr 27 20:32:08 testnode1 kernel:  ? rcu_is_watching+0x11/0xb0
Apr 27 20:32:08 testnode1 kernel:  ? do_syscall_64+0x1ea/0x14c0
Apr 27 20:32:08 testnode1 kernel:  ? lockdep_hardirqs_on+0x88/0x130
Apr 27 20:32:08 testnode1 kernel:  ? do_syscall_64+0x208/0x14c0
Apr 27 20:32:08 testnode1 kernel:  ? do_syscall_64+0x32/0x14c0
Apr 27 20:32:08 testnode1 kernel:  ? preempt_count_add+0x7f/0x190
Apr 27 20:32:08 testnode1 kernel:  ? do_syscall_64+0x5d/0x14c0
Apr 27 20:32:08 testnode1 kernel:  ? do_syscall_64+0x8d/0x14c0
Apr 27 20:32:08 testnode1 kernel:  ? irqentry_exit+0xf1/0x720
Apr 27 20:32:08 testnode1 kernel:  entry_SYSCALL_64_after_hwframe+0x76/0x7e
Apr 27 20:32:08 testnode1 kernel: RIP: 0033:0x7f245cf99c5e
Apr 27 20:32:08 testnode1 kernel: Code: 4d 89 d8 e8 34 bd 00 00 4c 8b 5d f8 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 11 c9 c3 0f 1f 80 00 00 00 00 48 8b 45 10 0f 05 <c9> c3 83 e2 39 83 fa 08 75 e7 e8 13 ff ff ff 0f 1f 00 f3 0f 1e fa
Apr 27 20:32:08 testnode1 kernel: RSP: 002b:00007ffca6d9f6a0 EFLAGS: 00000202 ORIG_RAX: 0000000000000001
Apr 27 20:32:08 testnode1 kernel: RAX: ffffffffffffffda RBX: 00007f245d1639a6 RCX: 00007f245cf99c5e
Apr 27 20:32:08 testnode1 kernel: RDX: 0000000000000001 RSI: 00007f245d1639a6 RDI: 0000000000000003
Apr 27 20:32:08 testnode1 kernel: RBP: 00007ffca6d9f6b0 R08: 0000000000000000 R09: 0000000000000000
Apr 27 20:32:08 testnode1 kernel: R10: 0000000000000000 R11: 0000000000000202 R12: 000000003d0f6860
Apr 27 20:32:08 testnode1 kernel: R13: 000000003d0f8580 R14: 000000003d0f6680 R15: 0000000000000000
Apr 27 20:32:08 testnode1 kernel:  </TASK>

^ permalink raw reply

* Re: [RFC] crypto: skcipher multi-data-unit requests for dm-crypt
From: Herbert Xu @ 2026-04-27 11:28 UTC (permalink / raw)
  To: Leonid Ravich
  Cc: David S. Miller, Mike Snitzer, Mikulas Patocka, Alasdair Kergon,
	Ard Biesheuvel, Eric Biggers, Jens Axboe, Horia Geanta,
	Gilad Ben-Yossef, linux-crypto, dm-devel, linux-block
In-Reply-To: <20260427095622.27799-1-lravich@amazon.com>

On Mon, Apr 27, 2026 at 09:56:22AM +0000, Leonid Ravich wrote:
>
> Proposal
> ========
> 
> Add a data_unit_size field to struct skcipher_request:
> 
>   struct skcipher_request {
>       unsigned int cryptlen;
>       u8 *iv;
>       struct scatterlist *src;
>       struct scatterlist *dst;
> +     unsigned int data_unit_size;
>       struct crypto_async_request base;
>       void *__ctx[] CRYPTO_MINALIGN_ATTR;
>   };
> 
> When data_unit_size is 0, behavior is unchanged (cryptlen is one
> data unit). When data_unit_size is nonzero, cryptlen must be a
> multiple of data_unit_size. The IV applies to the first data unit.
> The crypto driver is responsible for incrementing the tweak per
> data unit according to the mode.
> 
> This mirrors the data_unit_size concept already present in struct
> blk_crypto_config for inline encryption. In blk-crypto the size
> is a property of the key configuration. Here it is per-request
> because dm-crypt may use different sector sizes across different
> device-mapper tables sharing the same tfm.

Yes I'm happy with this since it could also work for IPsec.

But before you invest too much energy in it it would be helpful
if you can get some proof-of-concept performance numbers so that
your effort is not wasted down the track.

Thanks,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* [RFC] crypto: skcipher multi-data-unit requests for dm-crypt
From: Leonid Ravich @ 2026-04-27  9:56 UTC (permalink / raw)
  To: Herbert Xu, David S. Miller
  Cc: Mike Snitzer, Mikulas Patocka, Alasdair Kergon, Ard Biesheuvel,
	Eric Biggers, Jens Axboe, Horia Geanta, Gilad Ben-Yossef,
	linux-crypto, dm-devel, linux-block

dm-crypt submits one skcipher request per sector. For XTS mode with
512-byte sectors, a large bio can contain hundreds of sectors, each
requiring a separate crypto_skcipher_encrypt() call with its own
request allocation, IV generation, and async callback.

On systems with asynchronous hardware crypto accelerators, the
actual encryption is fast and this per-request setup overhead
becomes the bottleneck. Reducing the number of crypto API calls
per bio significantly improves throughput for these configurations.

This problem was discussed in December 2016 by Binoy Jayan, Milan
Broz, and Herbert Xu:

  https://lkml.indiana.edu/hypermail/linux/kernel/1612.2/01912.html

Herbert suggested moving IV generation into the crypto API so that
dm-crypt could submit larger blocks. The essiv template (by Ard
Biesheuvel, merged in 5.4) was the first step. The multi-data-unit
request concept was never implemented.

Existing hardware support
=========================

Several upstream crypto drivers already have hardware support for
per-data-unit tweak management in XTS mode but cannot use it
because the crypto API has no way to communicate the data unit
size:

NXP CAAM (drivers/crypto/caam/) has a hardware sector_size
register in its XTS shared descriptor. The driver currently
hardcodes it to 32768 bytes with this comment:

  "Set sector size to a big value, practically disabling
   sector size segmentation in xts implementation. We cannot
   take full advantage of this HW feature with existing
   crypto API / dm-crypt SW architecture."

Arm CryptoCell (drivers/crypto/ccree/) programs the hardware
data unit size via set_xex_data_unit_size() in its HW descriptor.
The driver template structure already has a data_unit field,
currently unused.

HiSilicon SEC2 (drivers/crypto/hisilicon/sec2/) submits the full
cryptlen to hardware with internal tweak management and SG DMA
via hardware scatter-gather lists.

Intel QAT (drivers/crypto/intel/qat/) submits full cryptlen to
hardware XTS mode in one operation with SG buffer lists.

Proposal
========

Add a data_unit_size field to struct skcipher_request:

  struct skcipher_request {
      unsigned int cryptlen;
      u8 *iv;
      struct scatterlist *src;
      struct scatterlist *dst;
+     unsigned int data_unit_size;
      struct crypto_async_request base;
      void *__ctx[] CRYPTO_MINALIGN_ATTR;
  };

When data_unit_size is 0, behavior is unchanged (cryptlen is one
data unit). When data_unit_size is nonzero, cryptlen must be a
multiple of data_unit_size. The IV applies to the first data unit.
The crypto driver is responsible for incrementing the tweak per
data unit according to the mode.

This mirrors the data_unit_size concept already present in struct
blk_crypto_config for inline encryption. In blk-crypto the size
is a property of the key configuration. Here it is per-request
because dm-crypt may use different sector sizes across different
device-mapper tables sharing the same tfm.

Required changes
=================

1. crypto: skcipher - add data_unit_size to skcipher_request
   as described above. The skcipher layer validates that
   cryptlen is a multiple of data_unit_size before dispatching
   to the driver.

2. crypto: xts - handle data_unit_size > 0 in the generic
   software XTS template by looping internally per data unit.
   This provides a universal fallback so every xts(...)
   instantiation supports multi-data-unit. The tweak increment
   (gf128mul_x_ble) is already implemented in the template.
   Hardware drivers override this with native support.

3. crypto: testmgr - add multi-data-unit XTS test vectors
   that cross-validate against individual per-unit encryption.

4. crypto: drivers - CAAM, CryptoCell, and other drivers with
   hardware data-unit support can read req->data_unit_size and
   program their hardware registers accordingly. For CAAM this
   means setting the sector_size register to the actual value
   instead of the current 32768 workaround.

5. dm-crypt - build a multi-entry scatterlist from the bio's
   bio_vecs, generate the IV for the first sector, set
   data_unit_size to the sector size, and submit one request.
   The existing per-sector path remains for IV modes that
   require post-processing (lmk, tcw, elephant) and for AEAD
   integrity modes.

Thanks,

Leonid Ravich

^ permalink raw reply

* Re: [PATCH v2 1/3] scsi: scan: allocate sdev and starget on the NUMA node of the host adapter
From: John Garry @ 2026-04-27  8:26 UTC (permalink / raw)
  To: Sumit Saxena
  Cc: martin.petersen, axboe, linux-scsi, linux-block,
	mpi3mr-linuxdrv.pdl, James Rizzo
In-Reply-To: <CAL2rwxr+7tD1BfxKJCn70wyPT34=sfFMfUNAV63SAW0BxQ-0Ug@mail.gmail.com>

On 24/04/2026 12:33, Sumit Saxena wrote:
>> For the actual shost allocation, we still use kzalloc() in
>> scsi_host_alloc(). However, shost associated device is often a pci
>> device, and we probe pci devices in the same NUMA node it exists, and we
>> try NUMA local allocations by default, so nothing is needed to change
>> for the shost allocation - is this right?
> shost allocation is not an issue, I will drop changes related to it.

But you have not made any changes related shost allocation and I am 
questioning why.

Further to what I was saying, even though most shosts are based on PCI 
device, not all are and I think that it is worth making this same change 
for shost allocation.

> The tests indicate that at times sdev and starget are allocated to
> remote NUMA node.
> So, I will limit these changes to sdev and starget only.


^ permalink raw reply

* Re: [PATCH v2 2/3] dm-inlinecrypt: add target for inline block device encryption
From: Benjamin Marzinski @ 2026-04-27  5:23 UTC (permalink / raw)
  To: Linlin Zhang
  Cc: linux-block, ebiggers, mpatocka, gmazyland, linux-kernel,
	adrianvovk, dm-devel, quic_mdalam, israelr, hch, axboe
In-Reply-To: <20260410134031.2880675-3-linlin.zhang@oss.qualcomm.com>

On Fri, Apr 10, 2026 at 06:40:30AM -0700, Linlin Zhang wrote:
> From: Eric Biggers <ebiggers@google.com>
> 
> Add a new device-mapper target "dm-inlinecrypt" that is similar to
> dm-crypt but uses the blk-crypto API instead of the regular crypto API.
> This allows it to take advantage of inline encryption hardware such as
> that commonly built into UFS host controllers.
> 
> The table syntax matches dm-crypt's, but for now only a stripped-down
> set of parameters is supported.  For example, for now AES-256-XTS is the
> only supported cipher.
> 
> dm-inlinecrypt is based on Android's dm-default-key with the
> controversial passthrough support removed.  Note that due to the removal
> of passthrough support, use of dm-inlinecrypt in combination with
> fscrypt causes double encryption of file contents (similar to dm-crypt +
> fscrypt), with the fscrypt layer not being able to use the inline
> encryption hardware.  This makes dm-inlinecrypt unusable on systems such
> as Android that use fscrypt and where a more optimized approach is
> needed.  It is however suitable as a replacement for dm-crypt.
> 
> dm-inlinecrypt supports both keyring key and hex key, the former avoids
> the key to be exposed in dm-table message. Similar to dm-default-key in
> Android, it will fallabck to the software block crypto once the inline
> crypto hardware cannot support the expected cipher.
> 
> Test:
> dmsetup create inlinecrypt_logon --table "0 `blockdev --getsz $1` \
> inlinecrypt aes-xts-plain64 :64:logon:fde:dminlinecrypt_test_key 0 $1 0"
> 
> Signed-off-by: Eric Biggers <ebiggers@google.com>
> Signed-off-by: Linlin Zhang <linlin.zhang@oss.qualcomm.com>
> ---
>  drivers/md/Kconfig          |  10 +
>  drivers/md/Makefile         |   1 +
>  drivers/md/dm-inlinecrypt.c | 559 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 570 insertions(+)
>  create mode 100644 drivers/md/dm-inlinecrypt.c
<snip> 
> diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c
> new file mode 100644
> index 000000000000..b6e98fdf8af1
> --- /dev/null
> +++ b/drivers/md/dm-inlinecrypt.c
<snip>
> +static int inlinecrypt_map(struct dm_target *ti, struct bio *bio)
> +{
> +	const struct inlinecrypt_ctx *ctx = ti->private;
> +	sector_t sector_in_target;
> +	u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = {};
> +
> +	bio_set_dev(bio, ctx->dev->bdev);
> +
> +	/*
> +	 * If the bio is a device-level request which doesn't target a specific
> +	 * sector, there's nothing more to do.
> +	 */
> +	if (bio_sectors(bio) == 0)
> +		return DM_MAPIO_REMAPPED;
> +
> +	/*
> +	 * The bio should never have an encryption context already, since
> +	 * dm-inlinecrypt doesn't pass through any inline encryption
> +	 * capabilities to the layer above it.
> +	 */
> +	if (WARN_ON_ONCE(bio_has_crypt_ctx(bio)))
> +		return DM_MAPIO_KILL;
> +
> +	/* Map the bio's sector to the underlying device. (512-byte sectors) */
> +	sector_in_target = dm_target_offset(ti, bio->bi_iter.bi_sector);
> +	bio->bi_iter.bi_sector = ctx->start + sector_in_target;
> +	/*
> +	 * If the bio doesn't have any data (e.g. if it's a DISCARD request),
> +	 * there's nothing more to do.
> +	 */
> +	if (!bio_has_data(bio))
> +		return DM_MAPIO_REMAPPED;
> +
> +	/* Calculate the DUN and enforce data-unit (crypto sector) alignment. */
> +	dun[0] = ctx->iv_offset + sector_in_target; /* 512-byte sectors */
> +	if (dun[0] & ((ctx->sector_size >> SECTOR_SHIFT) - 1))
> +		return DM_MAPIO_KILL;
> +	dun[0] >>= ctx->sector_bits - SECTOR_SHIFT; /* crypto sectors */
> +
> +	/*
> +	 * This check isn't necessary as we should have calculated max_dun
> +	 * correctly, but be safe.
> +	 */
> +	if (WARN_ON_ONCE(dun[0] > ctx->max_dun))
> +		return DM_MAPIO_KILL;
> +
> +	bio_crypt_set_ctx(bio, &ctx->key, dun, GFP_NOIO);
> +
> +	/*
> +	 * Since we've added an encryption context to the bio and
> +	 * blk-crypto-fallback may be needed to process it, it's necessary to
> +	 * use the fallback-aware bio submission code rather than
> +	 * unconditionally returning DM_MAPIO_REMAPPED.
> +	 *
> +	 * To get the correct accounting for a dm target in the case where
> +	 * __blk_crypto_submit_bio() doesn't take ownership of the bio (returns
> +	 * true), call __blk_crypto_submit_bio() directly and return
> +	 * DM_MAPIO_REMAPPED in that case, rather than relying on
> +	 * blk_crypto_submit_bio() which calls submit_bio() in that case.
> +	 */
> +	if (__blk_crypto_submit_bio(bio))

This will still double account for fallback writes (which call
submit_bio() on the encrypted bios, and return DM_MAPIO_SUBMITTED here). 

-Ben

> +		return DM_MAPIO_REMAPPED;
> +	return DM_MAPIO_SUBMITTED;
> +}


^ permalink raw reply

* [PATCH v4] block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()
From: Sungwoo Kim @ 2026-04-27  4:09 UTC (permalink / raw)
  To: Jens Axboe, Keith Busch
  Cc: Sungwoo Kim, Chao Shi, Weidong Zhu, Dave Tian, linux-block,
	linux-kernel

pin_user_pages_fast() can partially succeed and return the number of
pages that were actually pinned. However, the bio_integrity_map_user()
does not handle this partial pinning. This leads to a general protection
fault since bvec_from_pages() dereferences an unpinned page address,
which is 0.

To fix this, add a check to verify that all requested memory is pinned.
If partial pinning occurs, unpin the memory and return -EFAULT.

Reproducer in blktest: https://github.com/linux-blktests/blktests/pull/244

Kernel Oops:

Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
CPU: 0 UID: 0 PID: 1061 Comm: nvme-passthroug Not tainted 7.0.0-11783-g90957f9314e8-dirty #16 PREEMPT(lazy)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
RIP: 0010:bio_integrity_map_user.cold+0x1b0/0x9d6

Fixes: 492c5d455969 ("block: bio-integrity: directly map user buffers")
Acked-by: Chao Shi <cshi008@fiu.edu>
Acked-by: Weidong Zhu <weizhu@fiu.edu>
Acked-by: Dave Tian <daveti@purdue.edu>
Signed-off-by: Sungwoo Kim <iam@sung-woo.kim>
---
V3: https://lore.kernel.org/linux-block/20260420020327.1667156-3-iam@sung-woo.kim/

V3->V4
- Addressed a sashiko's comments [1], if it makes sense.
- V3 wrongly assumed that iov_iter_extract_pages() always pins user
  memory.
- V3 insufficiently handled the return value range.

[1] https://sashiko.dev/#/patchset/20260420020327.1667156-3-iam%40sung-woo.kim

 block/bio-integrity.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/block/bio-integrity.c b/block/bio-integrity.c
index e54c6e06e1cb..766cc2d1d51c 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -403,6 +403,23 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter)
 	if (unlikely(ret < 0))
 		goto free_bvec;
 
+	/* Handle partial pinning. This can happen when pin_user_pages_fast()
+	 * returns fewer pages than requested
+	 */
+	if (user_backed_iter(iter) && unlikely(ret != bytes)) {
+		if (0 < ret) {
+			int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE);
+			int i;
+
+			for (i = 0; i < npinned; i++)
+				unpin_user_page(pages[i]);
+		}
+		if (pages != stack_pages)
+				kvfree(pages);
+		ret = -EFAULT;
+		goto free_bvec;
+	}
+
 	nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset,
 				   &is_p2p);
 	if (pages != stack_pages)
-- 
2.47.3


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox