* [linus:master] [block] b520c4eef8: blktests.scsi/002.fail
From: kernel test robot @ 2026-04-22 14:21 UTC (permalink / raw)
To: Christoph Hellwig
Cc: oe-lkp, lkp, linux-kernel, Jens Axboe, Martin K. Petersen,
linux-block, oliver.sang
Hello,
kernel test robot noticed "blktests.scsi/002.fail" on:
commit: b520c4eef83dd406591431f936de0908c3ed7fb9 ("block: split bio_alloc_bioset more clearly into a fast and slowpath")
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git master
[test failed on linus/master c1f49dea2b8f335813d3b348fd39117fb8efb428]
[test failed on linux-next/master c7275b05bc428c7373d97aa2da02d3a7fa6b9f66]
[test failed on fix commit 67807fbaf12719fca46a622d759484652b79c7c3]
in testcase: blktests
version: blktests-x86_64-1d68a36-1_20260417
with following parameters:
disk: 1HDD
test: scsi-002
config: x86_64-rhel-9.4-func
compiler: gcc-14
test machine: 4 threads Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz (Skylake) with 32G memory
(please refer to attached dmesg/kmsg for entire log/backtrace)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <oliver.sang@intel.com>
| Closes: https://lore.kernel.org/oe-lkp/202604222222.8ddedd9b-lkp@intel.com
2026-04-21 09:29:51 cd /lkp/benchmarks/blktests
2026-04-21 09:29:51 echo scsi/002
2026-04-21 09:29:51 ./check scsi/002
scsi/002 => sda1 (perform a SG_DXFER_FROM_DEV from the /dev/sg read-write interface)
scsi/002 => sda1 (perform a SG_DXFER_FROM_DEV from the /dev/sg read-write interface) [failed]
runtime ... 0.016s
--- tests/scsi/002.out 2026-04-17 08:24:43.000000000 +0000
+++ /lkp/benchmarks/blktests/results/sda1/scsi/002.out.bad 2026-04-21 09:29:51.412382400 +0000
@@ -1,3 +1,4 @@
Running scsi/002
-PASS
+write: Cannot allocate memory
+FAIL
Test complete
The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20260422/202604222222.8ddedd9b-lkp@intel.com
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH v2] cdrom, scsi: sr: propagate read-only status to block layer via set_disk_ro()
From: Daan De Meyer @ 2026-04-22 11:32 UTC (permalink / raw)
To: phil, martin.petersen
Cc: James.Bottomley, axboe, linux-scsi, linux-block, linux-kernel,
Daan De Meyer
In-Reply-To: <20260330133403.796330-1-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>
---
v1 was two separate patches:
1. scsi: sr: propagate read-only status to block layer via set_disk_ro()
https://lore.kernel.org/linux-scsi/20260330122124.755083-1-daan@amutable.com/
2. scsi: sr: exclude CDC_MRW_W and CDC_RAM from writeable check
https://lore.kernel.org/linux-scsi/20260330133403.796330-1-daan@amutable.com/
Changes since v1:
- Addressed review feedback that the writable-state decision was split
between sr.c and cdrom.c. Moved the MMC GET CONFIGURATION probing out
of cdrom_open_write() into a new exported helper,
cdrom_probe_write_features(), which sr_probe() calls right after
get_capabilities(). register_cdrom() now computes the read-only state
from the complete mask, so the whole decision lives in cdrom.c.
- Since CDF_MRW and CDF_RWRT (GET CONFIGURATION with RT=00) report
drive-level capabilities that are persistent across media, a single
probe before register_cdrom() is sufficient; the redundant probe at
open time is dropped rather than duplicated.
- Squashed the two v1 patches into one commit to avoid a bisection
window where sr's narrowed mask check disagreed with the yet-to-be-
added cdrom.c logic.
- Dropped sr's cd->writeable flag entirely. It was always set to 1 in
practice (because CDC_MRW_W and CDC_RAM aren't populated by MODE
SENSE and default to "present"), so the gate in sr_init_command()
never rejected anything. Replaced with get_disk_ro(cd->disk), which
is now accurate and also covers kernel-internal bio writers that
bypass blkdev_write_iter()'s bdev_read_only() check.
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
* [syzbot] Monthly nbd report (Apr 2026)
From: syzbot @ 2026-04-22 9:08 UTC (permalink / raw)
To: josef, linux-block, linux-kernel, nbd, syzkaller-bugs
Hello nbd maintainers/developers,
This is a 31-day syzbot report for the nbd subsystem.
All related reports/information can be found at:
https://syzkaller.appspot.com/upstream/s/nbd
During the period, 0 new issues were detected and 0 were fixed.
In total, 5 issues are still open and 9 have already been fixed.
Some of the still happening issues:
Ref Crashes Repro Title
<1> 3815 Yes possible deadlock in pcpu_alloc_noprof (2)
https://syzkaller.appspot.com/bug?extid=91771b3fb86ec2dd7227
<2> 724 Yes INFO: task hung in nbd_queue_rq
https://syzkaller.appspot.com/bug?extid=30c16035531e3248dcbc
<3> 205 Yes possible deadlock in nbd_queue_rq
https://syzkaller.appspot.com/bug?extid=3dbc6142c85cc77eaf04
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
To disable reminders for individual bugs, reply with the following command:
#syz set <Ref> no-reminders
To change bug's subsystems, reply with:
#syz set <Ref> subsystems: new-subsystem
You may send multiple commands in a single email message.
^ permalink raw reply
* Re: [PATCH v6 01/43] fscrypt: add per-extent encryption support
From: Daniel Vacek @ 2026-04-22 8:17 UTC (permalink / raw)
To: Eric Biggers
Cc: Chris Mason, Josef Bacik, Theodore Y. Ts'o, Jaegeuk Kim,
Jens Axboe, David Sterba, linux-block, linux-fscrypt, linux-btrfs,
linux-kernel
In-Reply-To: <20260221221153.GA2123@quark>
On Sat, 21 Feb 2026 at 23:11, Eric Biggers <ebiggers@kernel.org> wrote:
> I lost all my original comments on this patch due to a computer crash,
> so apologies if this sounds a bit rushed. (I should know better than to
> run the latest mainline kernel. Would be nice if kernel developers
> focused on quality over new features...)
Oh, and I somehow missed this email in my mailbox :-/
I'm sorry about the late reply.
> > +/*
> > + * fscrypt_extent_context - the encryption context of an extent
> > + *
> > + * This is the on-disk information stored for an extent. The nonce is used as a
> > + * KDF input in conjuction with the inode context to derive a per-extent key for
> > + * encryption.
> > + *
> > + * With the current implementation, master_key_identifier and encryption mode
> > + * must match the inode context. These are here for future expansion where we
> > + * may want the option of mixing different keys and encryption modes for the
> > + * same file.
> > + */
>
> Above comment should document that this is used only when the filesystem
> uses per-extent encryption
Good point. I'll amend that.
> > +/**
> > + * fscrypt_set_bio_crypt_ctx_from_extent() - prepare a file contents bio for
> > + * inline crypto with extent
> > + * encryption
> > + * @bio: a bio which will eventually be submitted to the file
> > + * @ei: the extent's crypto info
> > + * @first_lblk: the first file logical block number in the I/O
>
> first_lblk probably should be 'pos' to match Christoph's pending patches
> (https://lore.kernel.org/linux-fscrypt/20260218061531.3318130-1-hch@lst.de).
> Either way, it also needs to be correctly documented to be an offset
> into the extent, not the file.
Noted, I'll clean it up for the next version.
> > + * If the contents of the file should be encrypted (or decrypted) with inline
> > + * encryption, then assign the appropriate encryption context to the bio.
>
> Above comment was copy-pasted and is misleading in its new context.
> This function assigns the encryption context unconditionally.
With the above cleanup I rather ended up fixing this in the code. For
some reason, the condition was added later in the series in the
btrfs-specific helper code which I removed and I put the condition
right here.
> > + * Normally the bio should be newly allocated (i.e. no pages added yet), as
> > + * otherwise fscrypt_mergeable_bio() won't work as intended.
>
> Likewise, copy-pasted comment that is misleading in the new context.
> It should refer to fscrypt_mergeable_extent_bio().
Yeah, I also noticed. Agreed and fixed.
> > +void fscrypt_set_bio_crypt_ctx_from_extent(struct bio *bio,
> > + const struct fscrypt_extent_info *ei,
> > + u64 first_lblk, gfp_t gfp_mask)
> > +{
> > + u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = { first_lblk };
>
> Above needs to calculate the DUN correctly when the data unit size is
> less than the file logical block size, or else the combination of
> sub-block data units and per-extent encryption needs to be explicitly
> not supported. Probably just the latter for now (it can be enforced by
> fscrypt_supported_v2_policy()).
With the above cleanup I replaced `first_lblk` with `pos >>
key->data_unit_size_bits`.
Though this still doesn't look endian-safe to me.
Note, per-extent encryption is only supported with the v2 policy.
> > +/**
> > + * fscrypt_mergeable_extent_bio() - test whether data can be added to a bio
> > + * @bio: the bio being built up
> > + * @ei: the fscrypt_extent_info for this extent
> > + * @next_lblk: the next file logical block number in the I/O
> > + *
> > + * When building a bio which may contain data which should undergo inline
> > + * encryption (or decryption) via fscrypt, filesystems should call this function
> > + * to ensure that the resulting bio contains only contiguous data unit numbers.
> > + * This will return false if the next part of the I/O cannot be merged with the
> > + * bio because either the encryption key would be different or the encryption
> > + * data unit numbers would be discontiguous.
> > + *
> > + * fscrypt_set_bio_crypt_ctx_from_extent() must have already been called on the
> > + * bio.
> > + *
> > + * This function isn't required in cases where crypto-mergeability is ensured in
> > + * another way, such as I/O targeting only a single file (and thus a single key)
> > + * combined with fscrypt_limit_io_blocks() to ensure DUN contiguity.
> > + *
> > + * Return: true iff the I/O is mergeable
> > + */
> > +bool fscrypt_mergeable_extent_bio(struct bio *bio,
> > + const struct fscrypt_extent_info *ei,
> > + u64 next_lblk)
> > +{
> > + const struct bio_crypt_ctx *bc = bio->bi_crypt_context;
> > + u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = { next_lblk };
> > +
> > + if (!ei)
> > + return true;
> > + if (!bc)
> > + return true;
> > +
> > + /*
> > + * Comparing the key pointers is good enough, as all I/O for each key
> > + * uses the same pointer. I.e., there's currently no need to support
> > + * merging requests where the keys are the same but the pointers differ.
> > + */
> > + if (bc->bc_key != ei->prep_key.blk_key)
> > + return false;
> > +
> > + return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_mergeable_extent_bio);
>
> Similar to fscrypt_set_bio_crypt_ctx_from_extent(). The copy-pasted
> comment needs to be updated to remove no-longer-relevant information
> specific to per-file encryption and correctly reflect per-extent
> encryption. The DUN needs to be calculated correctly for sub-block data
> units or else the combination of the two needs to be unsupported.
The DUN is fixed as per above. Regarding the comment, it looks quite
valid to me. What exactly would you like to change?
> > +static struct fscrypt_extent_info *
> > +setup_extent_info(struct inode *inode, const u8 nonce[FSCRYPT_FILE_NONCE_SIZE])
> > +{
> > + struct fscrypt_extent_info *ei;
> > + struct fscrypt_inode_info *ci;
> > + struct fscrypt_master_key *mk;
> > + u8 derived_key[FSCRYPT_MAX_RAW_KEY_SIZE];
> > + int err;
> > +
> > + ci = *fscrypt_inode_info_addr(inode);
> > + mk = ci->ci_master_key;
> > + if (WARN_ON_ONCE(!mk))
> > + return ERR_PTR(-ENOKEY);
> > +
> > + ei = kmem_cache_zalloc(fscrypt_extent_info_cachep, GFP_KERNEL);
> > + if (!ei)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + refcount_set(&ei->refs, 1);
> > + memcpy(ei->nonce, nonce, FSCRYPT_FILE_NONCE_SIZE);
> > + ei->sb = inode->i_sb;
> > +
> > + down_read(&mk->mk_sem);
> > + /*
> > + * We specifically don't check ->mk_present here because if the inode is
> > + * open and has a reference on the master key then it should be
> > + * available for us to use.
> > + */
>
> Above comment should be reworded to clarify that it is expected for
> ->mk_present to be either true or false here. As-is, it can be
> interpreted as meaning that checking ->mk_present is unnecessary because
> it is guaranteed to be true.
OK, I'll state that explicitly.
> The comment above struct fscrypt_master_key (which documents the
> different states the master key can be in) also needs to be updated to
> document that with filesystems that use per-extent encryption,
> ->mk_secret isn't wiped when the key is in the incompletely-removed
> state (and why that needs to be the case).
Right. I'll amend this in the respective patch.
> > +/**
> > + * fscrypt_prepare_new_extent() - prepare to create a new extent for a file
> > + * @inode: the possibly-encrypted inode
> > + *
> > + * If the inode is encrypted, setup the fscrypt_extent_info for a new extent.
> > + * This will include the nonce and the derived key necessary for the extent to
> > + * be encrypted. This is only meant to be used with inline crypto and on inodes
> > + * that need their contents encrypted.
> > + *
> > + * This doesn't persist the new extents encryption context, this is done later
> > + * by calling fscrypt_set_extent_context().
> > + *
> > + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if
> > + * we're not encrypted, or another -errno code
> > + */
> > +struct fscrypt_extent_info *fscrypt_prepare_new_extent(struct inode *inode)
> > +{
> > + u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> > +
> > + if (WARN_ON_ONCE(!*fscrypt_inode_info_addr(inode)))
> > + return ERR_PTR(-EOPNOTSUPP);
> > + if (WARN_ON_ONCE(!fscrypt_inode_uses_inline_crypto(inode)))
> > + return ERR_PTR(-EOPNOTSUPP);
> > +
> > + get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE);
> > + return setup_extent_info(inode, nonce);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent);
>
> Similarly, there seems to have been a lot of incorrect copy+pasting in
> the function comment. This new function requires that the caller *must*
> provide an encrypted inode, otherwise it WARNs. It can't be
> "possibly-encrypted".
That is indeed true.
> > +/**
> > + * fscrypt_load_extent_info() - create an fscrypt_extent_info from the context
> > + * @inode: the inode
> > + * @ctx: the context buffer
> > + * @ctx_size: the size of the context buffer
> > + *
> > + * Create the fscrypt_extent_info and derive the key based on the
> > + * fscrypt_extent_context buffer that is provided.
> > + *
> > + * Return: The newly allocated fscrypt_extent_info on success, -EOPNOTSUPP if
> > + * we're not encrypted, or another -errno code
> > + */
> > +struct fscrypt_extent_info *fscrypt_load_extent_info(struct inode *inode,
> > + u8 *ctx, size_t ctx_size)
>
> ctx should have type 'const u8 *'
Agreed.
> > +/**
> > + * fscrypt_set_extent_context() - Set the fscrypt extent context of a new extent
> > + * @inode: the inode this extent belongs to
> > + * @ei: the fscrypt_extent_info for the given extent
> > + * @buf: the buffer to copy the fscrypt extent context into
> > + *
> > + * This should be called after fscrypt_prepare_new_extent(), using the
> > + * fscrypt_extent_info that was created at that point.
> > + *
> > + * buf must be at most FSCRYPT_SET_CONTEXT_MAX_SIZE.
> > + *
> > + * Return: the size of the fscrypt_extent_context, errno if the inode has the
> > + * wrong policy version.
> > + */
> > +ssize_t fscrypt_context_for_new_extent(struct inode *inode,
> > + struct fscrypt_extent_info *ei, u8 *buf)
> > +{
> > + struct fscrypt_extent_context *ctx = (struct fscrypt_extent_context *)buf;
> > + const struct fscrypt_inode_info *ci = *fscrypt_inode_info_addr(inode);
> > +
> > + BUILD_BUG_ON(sizeof(struct fscrypt_extent_context) >
> > + FSCRYPT_SET_CONTEXT_MAX_SIZE);
> > +
> > + if (WARN_ON_ONCE(ci->ci_policy.version != 2))
> > + return -EINVAL;
> > +
> > + ctx->version = FSCRYPT_EXTENT_CONTEXT_V1;
> > + ctx->encryption_mode = ci->ci_policy.v2.contents_encryption_mode;
> > + memcpy(ctx->master_key_identifier,
> > + ci->ci_policy.v2.master_key_identifier,
> > + sizeof(ctx->master_key_identifier));
> > + memcpy(ctx->nonce, ei->nonce, FSCRYPT_FILE_NONCE_SIZE);
> > + return sizeof(struct fscrypt_extent_context);
> > +}
> > +EXPORT_SYMBOL_GPL(fscrypt_context_for_new_extent);
>
> The documentation "buf must be at most FSCRYPT_SET_CONTEXT_MAX_SIZE" is
> incorrect. It must actually be *at least* the size of
> 'struct fscrypt_extent_context'.
I see. While this looks like a typo, I understand it was meant to say
that it should never need to be more than that.
Perhaps something like this can be better worded:
@@ -812,7 +812,7 @@ EXPORT_SYMBOL_GPL(fscrypt_set_context);
* This should be called after fscrypt_prepare_new_extent(), using the
* fscrypt_extent_info that was created at that point.
*
- * buf must be at most FSCRYPT_SET_CONTEXT_MAX_SIZE.
+ * buf should be able to fit up to FSCRYPT_SET_CONTEXT_MAX_SIZE bytes.
*
* Return: the size of the fscrypt_extent_context, errno if the inode has the
* wrong policy version.
> Given that it's a fixed size, it probably would make sense to make the
> ouptut parameter reflect that: 'u8 out[FSCRYPT_EXTENT_CONTEXT_SIZE]'.
> Or even just use the struct itself.
Yeah, this looks like more of a future-proof feature. While the size
is fixed now, if we ever need to define a FSCRYPT_EXTENT_CONTEXT_V2
with different on-disk format (and size) we can still keep this very
same API.
> > + /*
> > + * If set then extent based encryption will be used for this file
> > + * system, and fs/crypto/ will enforce limits on the policies that are
> > + * allowed to be chosen. Currently this means only plain v2 policies
> > + * are supported.
> > + */
> > + unsigned int has_per_extent_encryption : 1;
>
> Needs clarification about what is meant by "plain". Some flags are
> supported (specifically the filename padding ones), some flags are not.
> All encryption modes still seem to be supported.
Maybe this?:
@@ -144,8 +144,8 @@ struct fscrypt_operations {
/*
* If set then extent based encryption will be used for this file
* system, and fs/crypto/ will enforce limits on the policies that are
- * allowed to be chosen. Currently this means only plain v2 policies
- * are supported.
+ * allowed to be chosen. Currently this means only v2 policies and a
+ * limited flags are supported.
*/
unsigned int has_per_extent_encryption : 1;
>
> > + if (count > 0 && inode->i_sb->s_cop->has_per_extent_encryption) {
> > + fscrypt_warn(inode,
> > + "Encryption flags aren't supported on file systems that use extent encryption");
> > + return false;
> > + }
>
> Similarly, this error message needs clarification. Some encryption
> flags are supported, some aren't.
Right. This should sound better:
@@ -248,7 +248,7 @@ static bool fscrypt_supported_v2_policy(const
struct fscrypt_policy_v2 *policy,
count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32);
if (count > 0 && inode->i_sb->s_cop->has_per_extent_encryption) {
fscrypt_warn(inode,
- "Encryption flags aren't supported on file systems
that use extent encryption");
+ "DIRECT_KEY and IV_INO_LBLK_* encryption flags
aren't supported on file systems that use extent encryption");
return false;
}
if (count > 1) {
Thanks for looking into it.
--nX
> - Eric
^ permalink raw reply
* Re: [PATCH] ublk: fix maple tree lockdep warning and unpin under spinlock
From: Shin'ichiro Kawasaki @ 2026-04-22 1:59 UTC (permalink / raw)
To: Ming Lei
Cc: Jens Axboe, linux-block, Caleb Sander Mateos, Ming Lei,
Liam R. Howlett
In-Reply-To: <20260422000856.2362220-1-tom.leiming@gmail.com>
On Apr 22, 2026 / 08:08, Ming Lei wrote:
> From: Ming Lei <ming.lei@redhat.com>
>
> Fix two issues in the shmem buffer maple tree usage:
>
> 1) ublk_buf_cleanup() iterates the tree with mas_for_each() without
> holding rcu_read_lock or mas_lock, triggering a lockdep splat on
> CONFIG_PROVE_RCU kernels. Add mas_lock/unlock around the iteration.
>
> 2) __ublk_ctrl_unreg_buf() calls unpin_user_pages() under mas_lock
> (a spinlock). unpin_user_pages can be expensive for large buffers
> and may take additional locks if folio refcount drops to zero.
> Restructure to drop mas_lock before unpinning, re-acquiring it
> to continue iteration.
>
> Both functions now use the same pattern: erase under lock, drop lock,
> unpin and free, re-lock to continue. Extract ublk_unpin_range_pages()
> helper to share the page unpinning loop.
>
> Reported-by: Jens Axboe <axboe@kernel.dk>
> Closes: https://lore.kernel.org/linux-block/0349d72d-dff8-4f9f-b448-919fa5ae96da@kernel.dk/
> Cc: Liam R. Howlett <liam.howlett@oracle.com>
> Signed-off-by: Ming Lei <ming.lei@redhat.com>
I observed the same WARN at blktests test cases block/033 and ublk/*. I
confirmed this patch avoids the WARN and makes the test cases pass. Thanks!
Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
I also bisected and confirmed that the commit below is the failure trigger.
Adding a Fixes tag might add a bit of value.
Fixes: 5e864438e285 ("ublk: replace xarray with IDA for shmem buffer index allocation")
^ permalink raw reply
* Re: [RFC PATCH v2 0/4] mm/zsmalloc: reduce zs_free() latency on swap release path
From: Xueyuan Chen @ 2026-04-22 0:34 UTC (permalink / raw)
To: nphamcs
Cc: ryncsn, haowenchao22, akpm, chengming.zhou, axboe, hannes,
minchan, senozhatsky, yosry, linux-block, linux-kernel, linux-mm,
baohua, xueyuan.chen21, haowenchao
In-Reply-To: <CAKEwX=P6L=CeJCXkC+cKbZKYk_ORho3pLy476c8=BbDianx=vg@mail.gmail.com>
On Tue, Apr 21, 2026 at 11:25:17AM -0700, Nhat Pham wrote:
[...]
>Hmm, free_zspage() and kmem_cache_free().
>
>* kmem_cache_free() is just handle freeing. Bulk-freeing?
>
>* free_zspage() looks like just ordinary teardown work :( Seems like
>we're not spinning any lock here - we just try lock the backing pages,
>and the rest is normal work. Not sure how to optimize this - perhaps
>deferring is the only way.
>
>
Hi Nhat,
Currently, free_zspage() is called while holding the class->lock.
However, free_zspage() eventually invokes folio_put(), which may acquire
the zone->lock.
This creates a nested lock dependency. If multiple CPUs contend for the
same class->lock and the current holder is stalled waiting for the
zone->lock, it significantly extends the hold time of the class->lock.
This causes other CPUs to wait much longer.
Here is the ftrace data showing the severe contention on class->lock.
Under contention, the time spent in queued_spin_lock_slowpath() jumps
from ~1.3us to over 30us, significantly increasing the total latency
of zs_free().
7) | zs_free() {
7) 0.220 us | _raw_read_lock();
7) | _raw_spin_lock() {
7) 1.320 us | queued_spin_lock_slowpath();
7) 1.820 us | }
7) 0.170 us | _raw_read_unlock();
7) 0.170 us | obj_free();
7) 0.190 us | fix_fullness_group();
7) 0.150 us | _raw_spin_unlock();
7) 0.170 us | kmem_cache_free();
7) 4.610 us | }
---------------------------------------------------------
7) | zs_free() {
7) 0.230 us | _raw_read_lock();
7) | _raw_spin_lock() {
7) + 30.100 us | queued_spin_lock_slowpath();
7) + 30.600 us | }
7) 0.200 us | _raw_read_unlock();
7) 0.170 us | obj_free();
7) 0.170 us | fix_fullness_group();
7) 0.170 us | _raw_spin_unlock();
7) 0.210 us | kmem_cache_free();
7) + 33.850 us | }
Best regards,
Xueyuan
^ permalink raw reply
* Re: [PATCH] ublk: optimize ublk_rq_has_data()
From: Caleb Sander Mateos @ 2026-04-22 0:31 UTC (permalink / raw)
To: Ming Lei; +Cc: Jens Axboe, linux-block, linux-kernel
In-Reply-To: <aegNf5C5-0ccxBPp@fedora>
On Tue, Apr 21, 2026 at 4:51 PM Ming Lei <tom.leiming@gmail.com> wrote:
>
> On Tue, Apr 21, 2026 at 01:47:42PM -0600, Caleb Sander Mateos wrote:
> > ublk_rq_has_data() currently uses bio_has_data(), which involves 2
> > indirections and several branches. Use blk_rq_bytes() instead, which
> > performs a single indirection with no branches.
> >
> > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> > ---
> > drivers/block/ublk_drv.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > index ef8a0705e68b..5f9b3c3876f4 100644
> > --- a/drivers/block/ublk_drv.c
> > +++ b/drivers/block/ublk_drv.c
> > @@ -1172,11 +1172,11 @@ static inline struct ublk_queue *ublk_get_queue(struct ublk_device *dev,
> > return dev->queues[qid];
> > }
> >
> > static inline bool ublk_rq_has_data(const struct request *rq)
> > {
> > - return bio_has_data(rq->bio);
> > + return blk_rq_bytes(rq);
> > }
>
> blk_rq_bytes() doesn't return the actual payload data bytes, such as,
> discard command, but bio_has_data() does.
So you're saying that on a discard, rq->__data_len is set to
bio->bi_iter.bi_size, which represents the number of bytes being
trimmed even though there's no data payload? I guess we could check
req_op(rq) here to at least avoid the bio indirection.
Thanks,
Caleb
^ permalink raw reply
* [PATCH] ublk: fix maple tree lockdep warning and unpin under spinlock
From: Ming Lei @ 2026-04-22 0:08 UTC (permalink / raw)
To: Jens Axboe, linux-block; +Cc: Caleb Sander Mateos, Ming Lei, Liam R. Howlett
From: Ming Lei <ming.lei@redhat.com>
Fix two issues in the shmem buffer maple tree usage:
1) ublk_buf_cleanup() iterates the tree with mas_for_each() without
holding rcu_read_lock or mas_lock, triggering a lockdep splat on
CONFIG_PROVE_RCU kernels. Add mas_lock/unlock around the iteration.
2) __ublk_ctrl_unreg_buf() calls unpin_user_pages() under mas_lock
(a spinlock). unpin_user_pages can be expensive for large buffers
and may take additional locks if folio refcount drops to zero.
Restructure to drop mas_lock before unpinning, re-acquiring it
to continue iteration.
Both functions now use the same pattern: erase under lock, drop lock,
unpin and free, re-lock to continue. Extract ublk_unpin_range_pages()
helper to share the page unpinning loop.
Reported-by: Jens Axboe <axboe@kernel.dk>
Closes: https://lore.kernel.org/linux-block/0349d72d-dff8-4f9f-b448-919fa5ae96da@kernel.dk/
Cc: Liam R. Howlett <liam.howlett@oracle.com>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
drivers/block/ublk_drv.c | 63 +++++++++++++++++++++++++---------------
1 file changed, 39 insertions(+), 24 deletions(-)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 49fb584e392b..0a92569c0c7d 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -5404,16 +5404,39 @@ static int ublk_ctrl_reg_buf(struct ublk_device *ub,
return ret;
}
+static void ublk_unpin_range_pages(unsigned long base_pfn,
+ unsigned long nr_pages)
+{
+#define UBLK_UNPIN_BATCH 32
+ struct page *pages[UBLK_UNPIN_BATCH];
+ unsigned long off;
+
+ for (off = 0; off < nr_pages; ) {
+ unsigned int batch = min_t(unsigned long,
+ nr_pages - off, UBLK_UNPIN_BATCH);
+ unsigned int j;
+
+ for (j = 0; j < batch; j++)
+ pages[j] = pfn_to_page(base_pfn + off + j);
+ unpin_user_pages(pages, batch);
+ off += batch;
+ }
+}
+
+/*
+ * Drop mas_lock during iteration to avoid unpinning pages under spinlock.
+ * Safe because callers hold ub->mutex (via ublk_lock_buf_tree), preventing
+ * concurrent tree modifications.
+ */
static int __ublk_ctrl_unreg_buf(struct ublk_device *ub, int buf_index)
{
MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
struct ublk_buf_range *range;
- struct page *pages[32];
int ret = -ENOENT;
mas_lock(&mas);
mas_for_each(&mas, range, ULONG_MAX) {
- unsigned long base, nr, off;
+ unsigned long base, nr;
if (range->buf_index != buf_index)
continue;
@@ -5422,18 +5445,12 @@ static int __ublk_ctrl_unreg_buf(struct ublk_device *ub, int buf_index)
base = mas.index;
nr = mas.last - base + 1;
mas_erase(&mas);
+ mas_unlock(&mas);
- for (off = 0; off < nr; ) {
- unsigned int batch = min_t(unsigned long,
- nr - off, 32);
- unsigned int j;
-
- for (j = 0; j < batch; j++)
- pages[j] = pfn_to_page(base + off + j);
- unpin_user_pages(pages, batch);
- off += batch;
- }
+ ublk_unpin_range_pages(base, nr);
kfree(range);
+
+ mas_lock(&mas);
}
mas_unlock(&mas);
@@ -5463,29 +5480,27 @@ static int ublk_ctrl_unreg_buf(struct ublk_device *ub,
return ret;
}
+/*
+ * Drop mas_lock during iteration to avoid unpinning pages under spinlock.
+ * Safe because this is called from device release with exclusive access.
+ */
static void ublk_buf_cleanup(struct ublk_device *ub)
{
MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
struct ublk_buf_range *range;
- struct page *pages[32];
+ mas_lock(&mas);
mas_for_each(&mas, range, ULONG_MAX) {
unsigned long base = mas.index;
unsigned long nr = mas.last - base + 1;
- unsigned long off;
- for (off = 0; off < nr; ) {
- unsigned int batch = min_t(unsigned long,
- nr - off, 32);
- unsigned int j;
-
- for (j = 0; j < batch; j++)
- pages[j] = pfn_to_page(base + off + j);
- unpin_user_pages(pages, batch);
- off += batch;
- }
+ mas_erase(&mas);
+ mas_unlock(&mas);
+ ublk_unpin_range_pages(base, nr);
kfree(range);
+ mas_lock(&mas);
}
+ mas_unlock(&mas);
mtree_destroy(&ub->buf_tree);
ida_destroy(&ub->buf_ida);
}
--
2.53.0
^ permalink raw reply related
* Re: [PATCH] ublk: optimize ublk_rq_has_data()
From: Ming Lei @ 2026-04-21 23:51 UTC (permalink / raw)
To: Caleb Sander Mateos; +Cc: Jens Axboe, linux-block, linux-kernel
In-Reply-To: <20260421194744.1524637-1-csander@purestorage.com>
On Tue, Apr 21, 2026 at 01:47:42PM -0600, Caleb Sander Mateos wrote:
> ublk_rq_has_data() currently uses bio_has_data(), which involves 2
> indirections and several branches. Use blk_rq_bytes() instead, which
> performs a single indirection with no branches.
>
> Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
> ---
> drivers/block/ublk_drv.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index ef8a0705e68b..5f9b3c3876f4 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -1172,11 +1172,11 @@ static inline struct ublk_queue *ublk_get_queue(struct ublk_device *dev,
> return dev->queues[qid];
> }
>
> static inline bool ublk_rq_has_data(const struct request *rq)
> {
> - return bio_has_data(rq->bio);
> + return blk_rq_bytes(rq);
> }
blk_rq_bytes() doesn't return the actual payload data bytes, such as,
discard command, but bio_has_data() does.
thanks,
Ming
^ permalink raw reply
* Re: RCU warning off ublk_buf_cleanup() -> mas_for_each()
From: Jens Axboe @ 2026-04-21 22:03 UTC (permalink / raw)
To: Liam R. Howlett; +Cc: Ming Lei, io-uring, linux-block@vger.kernel.org
In-Reply-To: <oxree5gq4nysdwjdk6rfnxd5sy3rwqswjwn6wea4q2ieo2xka2@np4gvh23qsm7>
On 4/21/26 3:56 PM, Liam R. Howlett wrote:
> * Jens Axboe <axboe@kernel.dk> [260421 17:41]:
>> On 4/21/26 2:28 PM, Liam R. Howlett wrote:
>>> * Jens Axboe <axboe@kernel.dk> [260421 13:47]:
>>>> Hi Ming,
>>>>
>>>> Ran into the below running tests on the current tree:
>>>>
>>>> =============================
>>>> WARNING: suspicious RCU usage
>>>> 7.0.0+ #16 Tainted: G N
>>>> -----------------------------
>>>> lib/maple_tree.c:759 suspicious rcu_dereference_check() usage!
>>>>
>>>> other info that might help us debug this:
>>>>
>>>>
>>>> rcu_scheduler_active = 2, debug_locks = 1
>>>> 1 lock held by iou-wrk-55535/55536:
>>>> #0: ffff800085a451a0 (ublk_ctl_mutex){+.+.}-{4:4}, at: ublk_ctrl_del_dev+0xdc/0x2f8
>>>>
>>>> stack backtrace:
>>>> CPU: 4 UID: 0 PID: 55536 Comm: iou-wrk-55535 Tainted: G N 7.0.0+ #16 PREEMPT
>>>> Tainted: [N]=TEST
>>>> Hardware name: linux,dummy-virt (DT)
>>>> Call trace:
>>>> show_stack+0x1c/0x30 (C)
>>>> dump_stack_lvl+0x68/0x90
>>>> dump_stack+0x18/0x20
>>>> lockdep_rcu_suspicious+0x170/0x200
>>>> mas_walk+0x3f0/0x6a0
>>>> mas_find+0x1b4/0x6b0
>>>> ublk_buf_cleanup+0xe0/0x240
>>>> ublk_cdev_rel+0x34/0x1b0
>>>> device_release+0xa4/0x350
>>>> kobject_put+0x138/0x250
>>>> put_device+0x18/0x30
>>>> ublk_put_device+0x18/0x28
>>>> ublk_ctrl_del_dev+0x120/0x2f8
>>>> ublk_ctrl_uring_cmd+0x598/0x29b8
>>>> io_uring_cmd+0x1e0/0x468
>>>> __io_issue_sqe+0xa4/0x748
>>>> io_issue_sqe+0x80/0xf68
>>>> io_wq_submit_work+0x26c/0xdc8
>>>> io_worker_handle_work+0x334/0xf20
>>>> io_wq_worker+0x278/0x9e8
>>>> ret_from_fork+0x10/0x20
>>>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>>>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>>>> ublkb0: unable to read partition table
>>>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>>>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>>>> Buffer I/O error on dev ublkb0, logical block 512, async page read
>>>> Buffer I/O error on dev ublkb0, logical block 512, async page read
>>>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>>>> Buffer I/O error on dev ublkb0, logical block 512, async page read
>>>>
>>>> and I briefly looked at it, but then just gave up as a) the maple tree
>>>> documentation is not that detailed,
>>>
>>> Which documentation is lacking? I will fix it.
>>>
>>> I have user documentation in the Documentation directory while
>>> technical details are in the code.
>>
>> I went into the core-api/ and leafed through that, didn't have anything
>> on mas_for_each() that pertained to locking. Was hoping I'd find a table
>> of which parts of the API requires what in terms of locking or RCU.
>> There arent a whole lot of in-kernel users of it yet, so looking at
>> other places in the kernel wasn't very useful.
>
> There's several users and the test code. Too bad none of them helped.
There are several, but that's not very much for a kernel API. I didn't
look at the test code. If I was writing the code I would dug deeper, but
this isn't what this is. I just found an issue and the bare basics of
poking at the API and docs to check my assumptions.
>> Since this is a merge window regression, I really just passed it to Ming
>> with you on the CC just in case, and didn't spend any more time on it.
>> I'm not the one that's supposed to be finding issues like this...
>>
>
> I appreciate the response as I'll try to cater the doc to what users
> search for.
A great example is what the llist stuff does in terms of which parts of
the API needs what kind of exclusion against each other, if any. But
maybe not as appropriate here.
And obviously my use case may not be entirely representative in terms of
what a developer might normally want if they were writing new code and
looking to use it. Or convert existing code.
> There are two APIs outlined in the documentation normal api with mtree_
> and the advanced api with mas_. The locking is outlined in each
> section.
If I was writing code using it, I'm sure it would've been fine. And yes
mas_find() lists it, but I'm just looking at a basic back trace and the
main user, which was mas_for_each().
> I guess a note stating to check your code with lockdep might be in order
> in the documentation.
That's always a good suggestion. And the rcu checking, as you'd probably
use both.
>>>> and b) other in-tree users also just
>>>> call mas_for_each() without either a lock held or RCU read side locked.
>>>
>>> mas_for_each() must hold a lock of some type.
>>
>> That's what I assumed. I missed that the rcu dereference check
>> checks for an external lock too, which I guess you can register
>> with the maple tree. Funky... I guess it's just for lockdep
>> purposes, makes sense then.
>
> The external lock is so that people can use the tree (which needs to
> allocate) with sleeping locks and not just spinlocks. Willy wants me to
> kill it one day, though.
Ah, so it's actually used. I'd have to agree with willy on that front.
Though I'm always annoyed at the forced locking that xarray imposes on
you, so...
>> Presumably the current use case is fine, as it's serialized
>> teardown. It just ends up triggering the rcu sanity checks.
>
> On tear down, we should iterate through and free any resources then
> destroy the tree. If you erase each element one at a time you will
> rebalance the tree - since it's a b-tree.
>
> Thanks again for taking the time to tell me where/what you looked so I
> can better answer the questions in the docs. At least the LLM found it
> for you.
LLM was just a way to save my time. I think I've spent longer writing
emails about it at this point ;-). Thanks for the quick replies, I'm
sure Ming will appreciate it and sort this out.
--
Jens Axboe
^ permalink raw reply
* Re: RCU warning off ublk_buf_cleanup() -> mas_for_each()
From: Liam R. Howlett @ 2026-04-21 21:56 UTC (permalink / raw)
To: Jens Axboe; +Cc: Ming Lei, io-uring, linux-block@vger.kernel.org
In-Reply-To: <6c1eaf1c-5c1e-4ca3-a9b6-b0305fcce588@kernel.dk>
* Jens Axboe <axboe@kernel.dk> [260421 17:41]:
> On 4/21/26 2:28 PM, Liam R. Howlett wrote:
> > * Jens Axboe <axboe@kernel.dk> [260421 13:47]:
> >> Hi Ming,
> >>
> >> Ran into the below running tests on the current tree:
> >>
> >> =============================
> >> WARNING: suspicious RCU usage
> >> 7.0.0+ #16 Tainted: G N
> >> -----------------------------
> >> lib/maple_tree.c:759 suspicious rcu_dereference_check() usage!
> >>
> >> other info that might help us debug this:
> >>
> >>
> >> rcu_scheduler_active = 2, debug_locks = 1
> >> 1 lock held by iou-wrk-55535/55536:
> >> #0: ffff800085a451a0 (ublk_ctl_mutex){+.+.}-{4:4}, at: ublk_ctrl_del_dev+0xdc/0x2f8
> >>
> >> stack backtrace:
> >> CPU: 4 UID: 0 PID: 55536 Comm: iou-wrk-55535 Tainted: G N 7.0.0+ #16 PREEMPT
> >> Tainted: [N]=TEST
> >> Hardware name: linux,dummy-virt (DT)
> >> Call trace:
> >> show_stack+0x1c/0x30 (C)
> >> dump_stack_lvl+0x68/0x90
> >> dump_stack+0x18/0x20
> >> lockdep_rcu_suspicious+0x170/0x200
> >> mas_walk+0x3f0/0x6a0
> >> mas_find+0x1b4/0x6b0
> >> ublk_buf_cleanup+0xe0/0x240
> >> ublk_cdev_rel+0x34/0x1b0
> >> device_release+0xa4/0x350
> >> kobject_put+0x138/0x250
> >> put_device+0x18/0x30
> >> ublk_put_device+0x18/0x28
> >> ublk_ctrl_del_dev+0x120/0x2f8
> >> ublk_ctrl_uring_cmd+0x598/0x29b8
> >> io_uring_cmd+0x1e0/0x468
> >> __io_issue_sqe+0xa4/0x748
> >> io_issue_sqe+0x80/0xf68
> >> io_wq_submit_work+0x26c/0xdc8
> >> io_worker_handle_work+0x334/0xf20
> >> io_wq_worker+0x278/0x9e8
> >> ret_from_fork+0x10/0x20
> >> Buffer I/O error on dev ublkb0, logical block 0, async page read
> >> Buffer I/O error on dev ublkb0, logical block 0, async page read
> >> ublkb0: unable to read partition table
> >> Buffer I/O error on dev ublkb0, logical block 0, async page read
> >> Buffer I/O error on dev ublkb0, logical block 0, async page read
> >> Buffer I/O error on dev ublkb0, logical block 512, async page read
> >> Buffer I/O error on dev ublkb0, logical block 512, async page read
> >> Buffer I/O error on dev ublkb0, logical block 0, async page read
> >> Buffer I/O error on dev ublkb0, logical block 512, async page read
> >>
> >> and I briefly looked at it, but then just gave up as a) the maple tree
> >> documentation is not that detailed,
> >
> > Which documentation is lacking? I will fix it.
> >
> > I have user documentation in the Documentation directory while
> > technical details are in the code.
>
> I went into the core-api/ and leafed through that, didn't have anything
> on mas_for_each() that pertained to locking. Was hoping I'd find a table
> of which parts of the API requires what in terms of locking or RCU.
> There arent a whole lot of in-kernel users of it yet, so looking at
> other places in the kernel wasn't very useful.
There's several users and the test code. Too bad none of them helped.
>
> Since this is a merge window regression, I really just passed it to Ming
> with you on the CC just in case, and didn't spend any more time on it.
> I'm not the one that's supposed to be finding issues like this...
>
I appreciate the response as I'll try to cater the doc to what users
search for.
There are two APIs outlined in the documentation normal api with mtree_
and the advanced api with mas_. The locking is outlined in each
section.
I guess a note stating to check your code with lockdep might be in order
in the documentation.
> >> and b) other in-tree users also just
> >> call mas_for_each() without either a lock held or RCU read side locked.
> >
> > mas_for_each() must hold a lock of some type.
>
> That's what I assumed. I missed that the rcu dereference check
> checks for an external lock too, which I guess you can register
> with the maple tree. Funky... I guess it's just for lockdep
> purposes, makes sense then.
The external lock is so that people can use the tree (which needs to
allocate) with sleeping locks and not just spinlocks. Willy wants me to
kill it one day, though.
>
> Presumably the current use case is fine, as it's serialized
> teardown. It just ends up triggering the rcu sanity checks.
On tear down, we should iterate through and free any resources then
destroy the tree. If you erase each element one at a time you will
rebalance the tree - since it's a b-tree.
Thanks again for taking the time to tell me where/what you looked so I
can better answer the questions in the docs. At least the LLM found it
for you.
Thanks,
Liam
^ permalink raw reply
* Re: [RFC PATCH v2 2/4] mm/zsmalloc: introduce zs_free_deferred() for async handle freeing
From: Barry Song @ 2026-04-21 21:42 UTC (permalink / raw)
To: Nhat Pham
Cc: Wenchao Hao, Andrew Morton, Chengming Zhou, Jens Axboe,
Johannes Weiner, Minchan Kim, Sergey Senozhatsky, Yosry Ahmed,
linux-block, linux-kernel, linux-mm, Xueyuan Chen, Wenchao Hao
In-Reply-To: <CAKEwX=P8_5qyou0KgvwvoPpRsUN4ohwsC7ysxjTW=Fbwk2uSOQ@mail.gmail.com>
On Wed, Apr 22, 2026 at 3:47 AM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Tue, Apr 21, 2026 at 5:16 AM Wenchao Hao <haowenchao22@gmail.com> wrote:
> >
> > zs_free() is expensive due to internal locking (pool->lock, class->lock)
> > and potential zspage freeing. On the process exit path, the slow
> > zs_free() blocks memory reclamation, delaying overall memory release.
> > This has been reported to significantly impact Android low-memory
> > killing where slot_free() accounts for over 80% of the total swap
> > entry freeing cost.
> >
> > Introduce zs_free_deferred() which queues handles into a fixed-size
> > per-pool array for later processing by a workqueue. This allows callers
> > to defer the expensive zs_free() and return quickly, so the process
> > exit path can release memory faster. The array capacity is derived from
> > a 128MB uncompressed data budget (128MB >> PAGE_SHIFT entries), which
> > scales naturally with PAGE_SIZE. When the array reaches half capacity,
> > the workqueue is scheduled to drain pending handles.
> >
> > zs_free_deferred() uses spin_trylock() to access the deferred queue.
> > If the lock is contended (e.g. drain in progress) or the queue is full,
> > it falls back to synchronous zs_free() to guarantee correctness.
> >
> > Also introduce zs_free_deferred_flush() for use during pool teardown to
> > ensure all pending handles are freed.
>
> Hmmm per-pool workqueue.
>
> Does that mean that if you only have one zs pool (in the case of
> zswap, or if you only have one zram device), you'll have less
> concurrency in freeing up zsmalloc memory for process teardown? Would
> this be problematic?
I believe so, as reported in the original email from Lei and Zhiguo,
which proposed introducing a swap entries list for async free.
>
> I think Kairui was also suggesting per-cpu-fying these batches/queues.
I guess a per–size-class workqueue might strike a balance
between scalability and reducing lock contention across
multiple classes, where the locks actually reside.
Thanks
Barry
^ permalink raw reply
* Re: RCU warning off ublk_buf_cleanup() -> mas_for_each()
From: Jens Axboe @ 2026-04-21 21:41 UTC (permalink / raw)
To: Liam R. Howlett; +Cc: Ming Lei, io-uring, linux-block@vger.kernel.org
In-Reply-To: <qyob3dbqkicviyjs77q6mmxldtwm6qdpgwznzw6ulipztphlbl@nb4bzctzlsnw>
On 4/21/26 2:28 PM, Liam R. Howlett wrote:
> * Jens Axboe <axboe@kernel.dk> [260421 13:47]:
>> Hi Ming,
>>
>> Ran into the below running tests on the current tree:
>>
>> =============================
>> WARNING: suspicious RCU usage
>> 7.0.0+ #16 Tainted: G N
>> -----------------------------
>> lib/maple_tree.c:759 suspicious rcu_dereference_check() usage!
>>
>> other info that might help us debug this:
>>
>>
>> rcu_scheduler_active = 2, debug_locks = 1
>> 1 lock held by iou-wrk-55535/55536:
>> #0: ffff800085a451a0 (ublk_ctl_mutex){+.+.}-{4:4}, at: ublk_ctrl_del_dev+0xdc/0x2f8
>>
>> stack backtrace:
>> CPU: 4 UID: 0 PID: 55536 Comm: iou-wrk-55535 Tainted: G N 7.0.0+ #16 PREEMPT
>> Tainted: [N]=TEST
>> Hardware name: linux,dummy-virt (DT)
>> Call trace:
>> show_stack+0x1c/0x30 (C)
>> dump_stack_lvl+0x68/0x90
>> dump_stack+0x18/0x20
>> lockdep_rcu_suspicious+0x170/0x200
>> mas_walk+0x3f0/0x6a0
>> mas_find+0x1b4/0x6b0
>> ublk_buf_cleanup+0xe0/0x240
>> ublk_cdev_rel+0x34/0x1b0
>> device_release+0xa4/0x350
>> kobject_put+0x138/0x250
>> put_device+0x18/0x30
>> ublk_put_device+0x18/0x28
>> ublk_ctrl_del_dev+0x120/0x2f8
>> ublk_ctrl_uring_cmd+0x598/0x29b8
>> io_uring_cmd+0x1e0/0x468
>> __io_issue_sqe+0xa4/0x748
>> io_issue_sqe+0x80/0xf68
>> io_wq_submit_work+0x26c/0xdc8
>> io_worker_handle_work+0x334/0xf20
>> io_wq_worker+0x278/0x9e8
>> ret_from_fork+0x10/0x20
>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>> ublkb0: unable to read partition table
>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>> Buffer I/O error on dev ublkb0, logical block 512, async page read
>> Buffer I/O error on dev ublkb0, logical block 512, async page read
>> Buffer I/O error on dev ublkb0, logical block 0, async page read
>> Buffer I/O error on dev ublkb0, logical block 512, async page read
>>
>> and I briefly looked at it, but then just gave up as a) the maple tree
>> documentation is not that detailed,
>
> Which documentation is lacking? I will fix it.
>
> I have user documentation in the Documentation directory while
> technical details are in the code.
I went into the core-api/ and leafed through that, didn't have anything
on mas_for_each() that pertained to locking. Was hoping I'd find a table
of which parts of the API requires what in terms of locking or RCU.
There arent a whole lot of in-kernel users of it yet, so looking at
other places in the kernel wasn't very useful.
Since this is a merge window regression, I really just passed it to Ming
with you on the CC just in case, and didn't spend any more time on it.
I'm not the one that's supposed to be finding issues like this...
>> and b) other in-tree users also just
>> call mas_for_each() without either a lock held or RCU read side locked.
>
> mas_for_each() must hold a lock of some type.
That's what I assumed. I missed that the rcu dereference check
checks for an external lock too, which I guess you can register
with the maple tree. Funky... I guess it's just for lockdep
purposes, makes sense then.
Presumably the current use case is fine, as it's serialized
teardown. It just ends up triggering the rcu sanity checks.
--
Jens Axboe
^ permalink raw reply
* Re: RCU warning off ublk_buf_cleanup() -> mas_for_each()
From: Liam R. Howlett @ 2026-04-21 20:28 UTC (permalink / raw)
To: Jens Axboe; +Cc: Ming Lei, io-uring, linux-block@vger.kernel.org
In-Reply-To: <0349d72d-dff8-4f9f-b448-919fa5ae96da@kernel.dk>
* Jens Axboe <axboe@kernel.dk> [260421 13:47]:
> Hi Ming,
>
> Ran into the below running tests on the current tree:
>
> =============================
> WARNING: suspicious RCU usage
> 7.0.0+ #16 Tainted: G N
> -----------------------------
> lib/maple_tree.c:759 suspicious rcu_dereference_check() usage!
>
> other info that might help us debug this:
>
>
> rcu_scheduler_active = 2, debug_locks = 1
> 1 lock held by iou-wrk-55535/55536:
> #0: ffff800085a451a0 (ublk_ctl_mutex){+.+.}-{4:4}, at: ublk_ctrl_del_dev+0xdc/0x2f8
>
> stack backtrace:
> CPU: 4 UID: 0 PID: 55536 Comm: iou-wrk-55535 Tainted: G N 7.0.0+ #16 PREEMPT
> Tainted: [N]=TEST
> Hardware name: linux,dummy-virt (DT)
> Call trace:
> show_stack+0x1c/0x30 (C)
> dump_stack_lvl+0x68/0x90
> dump_stack+0x18/0x20
> lockdep_rcu_suspicious+0x170/0x200
> mas_walk+0x3f0/0x6a0
> mas_find+0x1b4/0x6b0
> ublk_buf_cleanup+0xe0/0x240
> ublk_cdev_rel+0x34/0x1b0
> device_release+0xa4/0x350
> kobject_put+0x138/0x250
> put_device+0x18/0x30
> ublk_put_device+0x18/0x28
> ublk_ctrl_del_dev+0x120/0x2f8
> ublk_ctrl_uring_cmd+0x598/0x29b8
> io_uring_cmd+0x1e0/0x468
> __io_issue_sqe+0xa4/0x748
> io_issue_sqe+0x80/0xf68
> io_wq_submit_work+0x26c/0xdc8
> io_worker_handle_work+0x334/0xf20
> io_wq_worker+0x278/0x9e8
> ret_from_fork+0x10/0x20
> Buffer I/O error on dev ublkb0, logical block 0, async page read
> Buffer I/O error on dev ublkb0, logical block 0, async page read
> ublkb0: unable to read partition table
> Buffer I/O error on dev ublkb0, logical block 0, async page read
> Buffer I/O error on dev ublkb0, logical block 0, async page read
> Buffer I/O error on dev ublkb0, logical block 512, async page read
> Buffer I/O error on dev ublkb0, logical block 512, async page read
> Buffer I/O error on dev ublkb0, logical block 0, async page read
> Buffer I/O error on dev ublkb0, logical block 512, async page read
>
> and I briefly looked at it, but then just gave up as a) the maple tree
> documentation is not that detailed,
Which documentation is lacking? I will fix it.
I have user documentation in the Documentation directory while
technical details are in the code.
>and b) other in-tree users also just
> call mas_for_each() without either a lock held or RCU read side locked.
mas_for_each() must hold a lock of some type.
>
> Adding Liam for shedding some light on this...
>
> --
> Jens Axboe
>
^ permalink raw reply
* [PATCH 3/3] selftests: ublk: add ublk auto integrity test
From: Caleb Sander Mateos @ 2026-04-21 20:09 UTC (permalink / raw)
To: Ming Lei, Shuah Khan
Cc: linux-kernel, linux-block, linux-kselftest, Caleb Sander Mateos
In-Reply-To: <20260421200901.1528842-1-csander@purestorage.com>
The end-to-end integrity ublk selftest test_integrity_02 requires a
relatively recent fio version to support I/O with integrity buffers. Add
a version test_integrity_03 that uses the block layer's auto integrity
path instead. The auto integrity code doesn't check the application tag,
and doesn't indicate the bad guard/ref tag (just returns EILSEQ). But
it's a good smoke-test of the ublk integrity code and provides coverage
of the auto integrity path as well.
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
tools/testing/selftests/ublk/Makefile | 1 +
.../selftests/ublk/test_integrity_03.sh | 103 ++++++++++++++++++
2 files changed, 104 insertions(+)
create mode 100755 tools/testing/selftests/ublk/test_integrity_03.sh
diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
index ec6a8ce83d38..6e4fe8d1fed1 100644
--- a/tools/testing/selftests/ublk/Makefile
+++ b/tools/testing/selftests/ublk/Makefile
@@ -35,10 +35,11 @@ TEST_PROGS += test_loop_05.sh
TEST_PROGS += test_loop_06.sh
TEST_PROGS += test_loop_07.sh
TEST_PROGS += test_integrity_01.sh
TEST_PROGS += test_integrity_02.sh
+TEST_PROGS += test_integrity_03.sh
TEST_PROGS += test_recover_01.sh
TEST_PROGS += test_recover_02.sh
TEST_PROGS += test_recover_03.sh
TEST_PROGS += test_recover_04.sh
diff --git a/tools/testing/selftests/ublk/test_integrity_03.sh b/tools/testing/selftests/ublk/test_integrity_03.sh
new file mode 100755
index 000000000000..10f02339ea2d
--- /dev/null
+++ b/tools/testing/selftests/ublk/test_integrity_03.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
+
+if ! _have_program fio; then
+ exit $UBLK_SKIP_CODE
+fi
+
+_test_fill_and_verify() {
+ fio --name fill --rw randwrite $fio_args > /dev/null
+ if [ $? != 0 ]; then
+ echo "fio fill failed"
+ ERR_CODE=255
+ return 1
+ fi
+
+ fio --name verify --rw randread $fio_args > /dev/null
+ if [ $? != 0 ]; then
+ echo "fio verify failed"
+ ERR_CODE=255
+ return 1
+ fi
+}
+
+_test_corrupted_reftag() {
+ local dd_reftag_args="bs=1 seek=58 count=6 oflag=dsync conv=notrunc status=none"
+
+ # Overwrite 6-byte reftag at offset 48 + 10 = 58
+ dd if=/dev/urandom "of=${UBLK_BACKFILES[1]}" $dd_reftag_args
+ if [ $? != 0 ]; then
+ echo "dd corrupted_reftag failed"
+ ERR_CODE=255
+ return 1
+ fi
+
+ if fio --name corrupted_reftag --rw randread $fio_args > /dev/null 2> "$fio_err"; then
+ echo "fio corrupted_reftag unexpectedly succeeded"
+ ERR_CODE=255
+ return 1
+ fi
+
+ if ! grep -q "$expected_err" "$fio_err"; then
+ echo "fio corrupted_reftag message not found: $expected_err"
+ ERR_CODE=255
+ return 1
+ fi
+
+ # Reset to 0
+ dd if=/dev/zero "of=${UBLK_BACKFILES[1]}" $dd_reftag_args
+ if [ $? != 0 ]; then
+ echo "dd restore corrupted_reftag failed"
+ ERR_CODE=255
+ return 1
+ fi
+}
+
+_test_corrupted_data() {
+ local dd_data_args="bs=512 count=1 oflag=direct,dsync conv=notrunc status=none"
+
+ dd if=/dev/zero "of=${UBLK_BACKFILES[0]}" $dd_data_args
+ if [ $? != 0 ]; then
+ echo "dd corrupted_data failed"
+ ERR_CODE=255
+ return 1
+ fi
+
+ if fio --name corrupted_data --rw randread $fio_args > /dev/null 2> "$fio_err"; then
+ echo "fio corrupted_data unexpectedly succeeded"
+ ERR_CODE=255
+ return 1
+ fi
+
+ if ! grep -q "$expected_err" "$fio_err"; then
+ echo "fio corrupted_data message not found: $expected_err"
+ ERR_CODE=255
+ return 1
+ fi
+}
+
+_prep_test "loop" "end-to-end auto integrity"
+
+_create_backfile 0 256M
+_create_backfile 1 32M # 256M * (64 integrity bytes / 512 data bytes)
+integrity_params="--integrity_capable --integrity_reftag
+ --metadata_size 64 --pi_offset 48 --csum_type nvme"
+dev_id=$(_add_ublk_dev -t loop -u $integrity_params "${UBLK_BACKFILES[@]}")
+_check_add_dev "$TID" $?
+
+fio_args="--ioengine libaio --direct 1 --bsrange 512-1M --iodepth 32
+ --filename /dev/ublkb$dev_id"
+fio_err=$(mktemp "${UBLK_TEST_DIR}"/fio_err_XXXXX)
+ERR_CODE=0
+
+expected_err="Invalid or incomplete multibyte or wide character: read offset=0"
+_test_fill_and_verify && \
+_test_corrupted_reftag && \
+_test_corrupted_data
+
+rm -f "$fio_err"
+
+_cleanup_test
+_show_result "$TID" $ERR_CODE
--
2.45.2
^ permalink raw reply related
* [PATCH 1/3] selftests: ublk: remove unused argument to _cleanup
From: Caleb Sander Mateos @ 2026-04-21 20:08 UTC (permalink / raw)
To: Ming Lei, Shuah Khan
Cc: linux-kernel, linux-block, linux-kselftest, Caleb Sander Mateos
In-Reply-To: <20260421200901.1528842-1-csander@purestorage.com>
The _cleanup helper function doesn't take any arguments, so drop them
from its callers.
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
tools/testing/selftests/ublk/test_batch_01.sh | 4 ++--
tools/testing/selftests/ublk/test_batch_02.sh | 2 +-
tools/testing/selftests/ublk/test_batch_03.sh | 2 +-
tools/testing/selftests/ublk/test_generic_02.sh | 4 ++--
tools/testing/selftests/ublk/test_generic_03.sh | 2 +-
tools/testing/selftests/ublk/test_generic_06.sh | 2 +-
tools/testing/selftests/ublk/test_generic_07.sh | 2 +-
tools/testing/selftests/ublk/test_generic_08.sh | 4 ++--
tools/testing/selftests/ublk/test_generic_09.sh | 2 +-
tools/testing/selftests/ublk/test_generic_10.sh | 2 +-
tools/testing/selftests/ublk/test_generic_12.sh | 4 ++--
tools/testing/selftests/ublk/test_generic_13.sh | 2 +-
tools/testing/selftests/ublk/test_generic_16.sh | 4 ++--
tools/testing/selftests/ublk/test_generic_17.sh | 2 +-
tools/testing/selftests/ublk/test_loop_01.sh | 2 +-
tools/testing/selftests/ublk/test_loop_02.sh | 2 +-
tools/testing/selftests/ublk/test_loop_03.sh | 2 +-
tools/testing/selftests/ublk/test_loop_04.sh | 2 +-
tools/testing/selftests/ublk/test_loop_05.sh | 2 +-
tools/testing/selftests/ublk/test_loop_06.sh | 2 +-
tools/testing/selftests/ublk/test_loop_07.sh | 2 +-
tools/testing/selftests/ublk/test_null_01.sh | 2 +-
tools/testing/selftests/ublk/test_null_02.sh | 2 +-
tools/testing/selftests/ublk/test_null_03.sh | 2 +-
tools/testing/selftests/ublk/test_part_01.sh | 4 ++--
tools/testing/selftests/ublk/test_part_02.sh | 2 +-
tools/testing/selftests/ublk/test_recover_01.sh | 2 +-
tools/testing/selftests/ublk/test_recover_02.sh | 2 +-
tools/testing/selftests/ublk/test_recover_03.sh | 2 +-
tools/testing/selftests/ublk/test_recover_04.sh | 2 +-
tools/testing/selftests/ublk/test_shmemzc_01.sh | 2 +-
tools/testing/selftests/ublk/test_shmemzc_02.sh | 2 +-
tools/testing/selftests/ublk/test_shmemzc_03.sh | 2 +-
tools/testing/selftests/ublk/test_shmemzc_04.sh | 2 +-
tools/testing/selftests/ublk/test_stress_01.sh | 2 +-
tools/testing/selftests/ublk/test_stress_02.sh | 2 +-
tools/testing/selftests/ublk/test_stress_03.sh | 2 +-
tools/testing/selftests/ublk/test_stress_04.sh | 2 +-
tools/testing/selftests/ublk/test_stress_05.sh | 2 +-
tools/testing/selftests/ublk/test_stress_06.sh | 2 +-
tools/testing/selftests/ublk/test_stress_07.sh | 2 +-
tools/testing/selftests/ublk/test_stress_08.sh | 2 +-
tools/testing/selftests/ublk/test_stress_09.sh | 2 +-
tools/testing/selftests/ublk/test_stripe_01.sh | 2 +-
tools/testing/selftests/ublk/test_stripe_02.sh | 2 +-
tools/testing/selftests/ublk/test_stripe_03.sh | 2 +-
tools/testing/selftests/ublk/test_stripe_04.sh | 2 +-
tools/testing/selftests/ublk/test_stripe_05.sh | 2 +-
tools/testing/selftests/ublk/test_stripe_06.sh | 2 +-
49 files changed, 55 insertions(+), 55 deletions(-)
diff --git a/tools/testing/selftests/ublk/test_batch_01.sh b/tools/testing/selftests/ublk/test_batch_01.sh
index a18fb39af8be..6e19303706a9 100755
--- a/tools/testing/selftests/ublk/test_batch_01.sh
+++ b/tools/testing/selftests/ublk/test_batch_01.sh
@@ -16,16 +16,16 @@ _create_backfile 1 256M
dev_id=$(_add_ublk_dev -t loop -q 2 -b "${UBLK_BACKFILES[0]}")
_check_add_dev $TID $?
if ! _mkfs_mount_test /dev/ublkb"${dev_id}"; then
- _cleanup_test "generic"
+ _cleanup_test
_show_result $TID 255
fi
dev_id=$(_add_ublk_dev -t stripe -b --auto_zc "${UBLK_BACKFILES[0]}" "${UBLK_BACKFILES[1]}")
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
-_cleanup_test "generic"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_batch_02.sh b/tools/testing/selftests/ublk/test_batch_02.sh
index 7ca384d11987..7c683f755379 100755
--- a/tools/testing/selftests/ublk/test_batch_02.sh
+++ b/tools/testing/selftests/ublk/test_batch_02.sh
@@ -23,7 +23,7 @@ _check_add_dev $TID $?
# run fio over the ublk disk
fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite \
--iodepth=32 --size=100M --numjobs=4 > /dev/null 2>&1
ERR_CODE=$?
-_cleanup_test "generic"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_batch_03.sh b/tools/testing/selftests/ublk/test_batch_03.sh
index aca9cf144b55..914ccd6a335d 100755
--- a/tools/testing/selftests/ublk/test_batch_03.sh
+++ b/tools/testing/selftests/ublk/test_batch_03.sh
@@ -23,7 +23,7 @@ _check_add_dev $TID $?
# run fio over the ublk disk
fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite \
--iodepth=32 --size=100M --numjobs=4 > /dev/null 2>&1
ERR_CODE=$?
-_cleanup_test "generic"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_02.sh b/tools/testing/selftests/ublk/test_generic_02.sh
index 46b657143fd6..2afc8cdbed8f 100755
--- a/tools/testing/selftests/ublk/test_generic_02.sh
+++ b/tools/testing/selftests/ublk/test_generic_02.sh
@@ -27,11 +27,11 @@ for _ in $(seq 100); do
grep -q "BPFTRACE_READY" "$UBLK_TMP" 2>/dev/null && break
sleep 0.1
done
if ! kill -0 "$btrace_pid" 2>/dev/null; then
- _cleanup_test "null"
+ _cleanup_test
exit "$UBLK_SKIP_CODE"
fi
# run fio over this ublk disk (pinned to CPU 0)
taskset -c 0 fio --name=write_seq \
@@ -49,7 +49,7 @@ wait
if grep -q "^out_of_order:" "$UBLK_TMP"; then
echo "I/O reordering detected:"
grep "^out_of_order:" "$UBLK_TMP"
ERR_CODE=255
fi
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_03.sh b/tools/testing/selftests/ublk/test_generic_03.sh
index 8934ea926762..8e78be860d34 100755
--- a/tools/testing/selftests/ublk/test_generic_03.sh
+++ b/tools/testing/selftests/ublk/test_generic_03.sh
@@ -21,7 +21,7 @@ if [ "$max_segments" != "32" ]; then
ERR_CODE=255
fi
if [ "$max_segment_size" != "32768" ]; then
ERR_CODE=255
fi
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_06.sh b/tools/testing/selftests/ublk/test_generic_06.sh
index 14a05054fcd8..a8b3634b6b4b 100755
--- a/tools/testing/selftests/ublk/test_generic_06.sh
+++ b/tools/testing/selftests/ublk/test_generic_06.sh
@@ -34,7 +34,7 @@ fi
if [ $ELAPSED -ge 5 ]; then
echo "dd took $ELAPSED seconds to exit (>= 5s tolerance)!"
ERR_CODE=255
fi
-_cleanup_test "fault_inject"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_07.sh b/tools/testing/selftests/ublk/test_generic_07.sh
index 8dcfd8978f50..d2c5e65bd124 100755
--- a/tools/testing/selftests/ublk/test_generic_07.sh
+++ b/tools/testing/selftests/ublk/test_generic_07.sh
@@ -21,7 +21,7 @@ ERR_CODE=$?
if [ "$ERR_CODE" -eq 0 ]; then
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
fi
-_cleanup_test "generic"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_08.sh b/tools/testing/selftests/ublk/test_generic_08.sh
index ce88c31d6b9c..77a18b313f3d 100755
--- a/tools/testing/selftests/ublk/test_generic_08.sh
+++ b/tools/testing/selftests/ublk/test_generic_08.sh
@@ -16,16 +16,16 @@ _create_backfile 1 256M
dev_id=$(_add_ublk_dev -t loop -q 2 --auto_zc "${UBLK_BACKFILES[0]}")
_check_add_dev $TID $?
if ! _mkfs_mount_test /dev/ublkb"${dev_id}"; then
- _cleanup_test "generic"
+ _cleanup_test
_show_result $TID 255
fi
dev_id=$(_add_ublk_dev -t stripe --auto_zc "${UBLK_BACKFILES[0]}" "${UBLK_BACKFILES[1]}")
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
-_cleanup_test "generic"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_09.sh b/tools/testing/selftests/ublk/test_generic_09.sh
index 744d0cdaa242..6c25242f245f 100755
--- a/tools/testing/selftests/ublk/test_generic_09.sh
+++ b/tools/testing/selftests/ublk/test_generic_09.sh
@@ -20,8 +20,8 @@ _check_add_dev $TID $?
# run fio over the two disks
fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1
ERR_CODE=$?
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_10.sh b/tools/testing/selftests/ublk/test_generic_10.sh
index 4b4293b9081f..fdabc9d9075e 100755
--- a/tools/testing/selftests/ublk/test_generic_10.sh
+++ b/tools/testing/selftests/ublk/test_generic_10.sh
@@ -23,7 +23,7 @@ fi
new_size=$(_get_disk_size /dev/ublkb"${dev_id}")
if [ "$new_size" != "$size" ]; then
ERR_CODE=255
fi
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_12.sh b/tools/testing/selftests/ublk/test_generic_12.sh
index 54b81ddfe9f9..435497f8da8d 100755
--- a/tools/testing/selftests/ublk/test_generic_12.sh
+++ b/tools/testing/selftests/ublk/test_generic_12.sh
@@ -23,11 +23,11 @@ dev_t=$(_get_disk_dev_t "$dev_id")
bpftrace trace/count_ios_per_tid.bt "$dev_t" > "$UBLK_TMP" 2>&1 &
btrace_pid=$!
sleep 2
if ! kill -0 "$btrace_pid" > /dev/null 2>&1; then
- _cleanup_test "null"
+ _cleanup_test
exit "$UBLK_SKIP_CODE"
fi
# do imbalanced I/O on the ublk device
# pin to cpu 0 to prevent migration/only target one queue
@@ -52,7 +52,7 @@ if [[ $NR_THREADS_THAT_HANDLED_IO -ne $NTHREADS ]]; then
echo "only $NR_THREADS_THAT_HANDLED_IO handled I/O! expected $NTHREADS"
cat "$UBLK_TMP"
ERR_CODE=255
fi
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_13.sh b/tools/testing/selftests/ublk/test_generic_13.sh
index 922115aa14f4..2c1be6286db8 100755
--- a/tools/testing/selftests/ublk/test_generic_13.sh
+++ b/tools/testing/selftests/ublk/test_generic_13.sh
@@ -13,7 +13,7 @@ if ${UBLK_PROG} features | grep -q unknown; then
echo "# this failure is expected if running an older test suite against"
echo "# a newer kernel with new features added"
ERR_CODE=255
fi
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_16.sh b/tools/testing/selftests/ublk/test_generic_16.sh
index 3ef367836ac5..6a4952146ea1 100755
--- a/tools/testing/selftests/ublk/test_generic_16.sh
+++ b/tools/testing/selftests/ublk/test_generic_16.sh
@@ -7,11 +7,11 @@ ERR_CODE=0
_prep_test "null" "stop --safe command"
# Check if SAFE_STOP_DEV feature is supported
if ! _have_feature "SAFE_STOP_DEV"; then
- _cleanup_test "null"
+ _cleanup_test
exit "$UBLK_SKIP_CODE"
fi
# Test 1: stop --safe on idle device should succeed
dev_id=$(_add_ublk_dev -t null -q 2 -d 32)
@@ -50,7 +50,7 @@ wait $dd_pid 2>/dev/null
# Now device should be idle, regular delete should work
_ublk_del_dev "${dev_id}"
udevadm settle
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_generic_17.sh b/tools/testing/selftests/ublk/test_generic_17.sh
index 2278b5fc9dba..b483d53a897a 100755
--- a/tools/testing/selftests/ublk/test_generic_17.sh
+++ b/tools/testing/selftests/ublk/test_generic_17.sh
@@ -29,7 +29,7 @@ fi
# Clean up the device. This can only succeed once teardown of the above
# exited ublk server completes. So if teardown never completes, we will
# time out here
_ublk_del_dev "${dev_id}"
-_cleanup_test "fault_inject"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_loop_01.sh b/tools/testing/selftests/ublk/test_loop_01.sh
index 338a235fd82a..c0f5b619ad6e 100755
--- a/tools/testing/selftests/ublk/test_loop_01.sh
+++ b/tools/testing/selftests/ublk/test_loop_01.sh
@@ -18,8 +18,8 @@ _check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M
ERR_CODE=$?
-_cleanup_test "loop"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_loop_02.sh b/tools/testing/selftests/ublk/test_loop_02.sh
index 04c52454e2ec..f4191ea71f50 100755
--- a/tools/testing/selftests/ublk/test_loop_02.sh
+++ b/tools/testing/selftests/ublk/test_loop_02.sh
@@ -12,8 +12,8 @@ dev_id=$(_add_ublk_dev -t loop "${UBLK_BACKFILES[0]}")
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
-_cleanup_test "loop"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_loop_03.sh b/tools/testing/selftests/ublk/test_loop_03.sh
index 6e8f649fe93d..aaac0c59a5ad 100755
--- a/tools/testing/selftests/ublk/test_loop_03.sh
+++ b/tools/testing/selftests/ublk/test_loop_03.sh
@@ -17,8 +17,8 @@ _check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M
ERR_CODE=$?
-_cleanup_test "loop"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_loop_04.sh b/tools/testing/selftests/ublk/test_loop_04.sh
index 9f6774ec0de6..f584c119f1d2 100755
--- a/tools/testing/selftests/ublk/test_loop_04.sh
+++ b/tools/testing/selftests/ublk/test_loop_04.sh
@@ -13,8 +13,8 @@ dev_id=$(_add_ublk_dev -t loop -z "${UBLK_BACKFILES[0]}")
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
-_cleanup_test "loop"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_loop_05.sh b/tools/testing/selftests/ublk/test_loop_05.sh
index 2b8d99e007be..ca1a5df5f9de 100755
--- a/tools/testing/selftests/ublk/test_loop_05.sh
+++ b/tools/testing/selftests/ublk/test_loop_05.sh
@@ -18,8 +18,8 @@ _check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M
ERR_CODE=$?
-_cleanup_test "loop"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_loop_06.sh b/tools/testing/selftests/ublk/test_loop_06.sh
index e73f6f4844db..26f710ba9db7 100755
--- a/tools/testing/selftests/ublk/test_loop_06.sh
+++ b/tools/testing/selftests/ublk/test_loop_06.sh
@@ -17,8 +17,8 @@ _check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M
ERR_CODE=$?
-_cleanup_test "loop"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_loop_07.sh b/tools/testing/selftests/ublk/test_loop_07.sh
index 264d20e7c530..a9ab0b671cb2 100755
--- a/tools/testing/selftests/ublk/test_loop_07.sh
+++ b/tools/testing/selftests/ublk/test_loop_07.sh
@@ -13,8 +13,8 @@ dev_id=$(_add_ublk_dev -t loop -u "${UBLK_BACKFILES[0]}")
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
-_cleanup_test "loop"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_null_01.sh b/tools/testing/selftests/ublk/test_null_01.sh
index eebce8076530..d2c38cbb2dd5 100755
--- a/tools/testing/selftests/ublk/test_null_01.sh
+++ b/tools/testing/selftests/ublk/test_null_01.sh
@@ -16,8 +16,8 @@ _check_add_dev $TID $?
# run fio over the two disks
fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1
ERR_CODE=$?
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_null_02.sh b/tools/testing/selftests/ublk/test_null_02.sh
index 654bdff39664..7b205ca56367 100755
--- a/tools/testing/selftests/ublk/test_null_02.sh
+++ b/tools/testing/selftests/ublk/test_null_02.sh
@@ -16,8 +16,8 @@ _check_add_dev $TID $?
# run fio over the two disks
fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1
ERR_CODE=$?
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_null_03.sh b/tools/testing/selftests/ublk/test_null_03.sh
index 29cd09f06672..eee7a87a60da 100755
--- a/tools/testing/selftests/ublk/test_null_03.sh
+++ b/tools/testing/selftests/ublk/test_null_03.sh
@@ -16,8 +16,8 @@ _check_add_dev $TID $?
# run fio over the two disks
fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1
ERR_CODE=$?
-_cleanup_test "null"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_part_01.sh b/tools/testing/selftests/ublk/test_part_01.sh
index 8028f6e4b3a5..fa3b1a9af894 100755
--- a/tools/testing/selftests/ublk/test_part_01.sh
+++ b/tools/testing/selftests/ublk/test_part_01.sh
@@ -80,11 +80,11 @@ if ! _have_program sfdisk || ! _have_program blockdev; then
fi
_prep_test "generic" "test UBLK_F_NO_AUTO_PART_SCAN"
if ! _have_feature "UBLK_F_NO_AUTO_PART_SCAN"; then
- _cleanup_test "generic"
+ _cleanup_test
exit "$UBLK_SKIP_CODE"
fi
# Create and format backing file with partition table
@@ -98,7 +98,7 @@ format_backing_file "${UBLK_BACKFILES[0]}"
# Test no auto partition scan with manual scan
[ "$ERR_CODE" -eq 0 ] && test_no_auto_part_scan "${UBLK_BACKFILES[0]}"
[ $? -ne 0 ] && ERR_CODE=255
-_cleanup_test "generic"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_part_02.sh b/tools/testing/selftests/ublk/test_part_02.sh
index 7d42ab4d6e83..d9ec06f36aee 100755
--- a/tools/testing/selftests/ublk/test_part_02.sh
+++ b/tools/testing/selftests/ublk/test_part_02.sh
@@ -61,7 +61,7 @@ _prep_test "partition_scan" "verify async partition scan prevents IO hang"
_test_partition_scan_no_hang "no" "DEAD"
# Test 2: With recovery support - should transition to QUIESCED
_test_partition_scan_no_hang "yes" "QUIESCED"
-_cleanup_test "partition_scan"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_recover_01.sh b/tools/testing/selftests/ublk/test_recover_01.sh
index 2672f9c40fa8..1cddc2345dab 100755
--- a/tools/testing/selftests/ublk/test_recover_01.sh
+++ b/tools/testing/selftests/ublk/test_recover_01.sh
@@ -38,7 +38,7 @@ wait
ublk_run_recover_test -t null -q 2 -r 1 -i 1 &
ublk_run_recover_test -t loop -q 2 -r 1 -i 1 "${UBLK_BACKFILES[0]}" &
ublk_run_recover_test -t stripe -q 2 -r 1 -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
-_cleanup_test "recover"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_recover_02.sh b/tools/testing/selftests/ublk/test_recover_02.sh
index bda5064bc31f..9c3f481880d3 100755
--- a/tools/testing/selftests/ublk/test_recover_02.sh
+++ b/tools/testing/selftests/ublk/test_recover_02.sh
@@ -42,7 +42,7 @@ wait
ublk_run_recover_test -t null -q 2 -r 1 -z -i 1 &
ublk_run_recover_test -t loop -q 2 -r 1 -z -i 1 "${UBLK_BACKFILES[0]}" &
ublk_run_recover_test -t stripe -q 2 -r 1 -z -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
-_cleanup_test "recover"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_recover_03.sh b/tools/testing/selftests/ublk/test_recover_03.sh
index e0dc0b8fe5d6..2554805e5b02 100755
--- a/tools/testing/selftests/ublk/test_recover_03.sh
+++ b/tools/testing/selftests/ublk/test_recover_03.sh
@@ -37,7 +37,7 @@ wait
ublk_run_quiesce_recover -t null -q 2 -r 1 -i 1 &
ublk_run_quiesce_recover -t loop -q 2 -r 1 -i 1 "${UBLK_BACKFILES[0]}" &
ublk_run_quiesce_recover -t stripe -q 2 -r 1 -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
-_cleanup_test "quiesce"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_recover_04.sh b/tools/testing/selftests/ublk/test_recover_04.sh
index 178443394ca5..4c83c1840c68 100755
--- a/tools/testing/selftests/ublk/test_recover_04.sh
+++ b/tools/testing/selftests/ublk/test_recover_04.sh
@@ -33,7 +33,7 @@ wait
ublk_run_recover_test -t null -q 2 -r 1 -u -i 1 &
ublk_run_recover_test -t loop -q 2 -r 1 -u -i 1 "${UBLK_BACKFILES[0]}" &
ublk_run_recover_test -t stripe -q 2 -r 1 -u -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
-_cleanup_test "recover"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_shmemzc_01.sh b/tools/testing/selftests/ublk/test_shmemzc_01.sh
index 47210af2aa20..b244ab3479a2 100755
--- a/tools/testing/selftests/ublk/test_shmemzc_01.sh
+++ b/tools/testing/selftests/ublk/test_shmemzc_01.sh
@@ -65,8 +65,8 @@ _ublk_del_dev "${dev_id}"
rm -f "$HTLB_FILE"
umount "$HTLB_MNT"
rmdir "$HTLB_MNT"
echo "$OLD_NR_HP" > /proc/sys/vm/nr_hugepages
-_cleanup_test "shmem_zc"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_shmemzc_02.sh b/tools/testing/selftests/ublk/test_shmemzc_02.sh
index aed9262494e9..810dccba6d84 100755
--- a/tools/testing/selftests/ublk/test_shmemzc_02.sh
+++ b/tools/testing/selftests/ublk/test_shmemzc_02.sh
@@ -61,8 +61,8 @@ _ublk_del_dev "${dev_id}"
rm -f "$HTLB_FILE"
umount "$HTLB_MNT"
rmdir "$HTLB_MNT"
echo "$OLD_NR_HP" > /proc/sys/vm/nr_hugepages
-_cleanup_test "shmem_zc"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_shmemzc_03.sh b/tools/testing/selftests/ublk/test_shmemzc_03.sh
index db967a9ffe81..606362491a32 100755
--- a/tools/testing/selftests/ublk/test_shmemzc_03.sh
+++ b/tools/testing/selftests/ublk/test_shmemzc_03.sh
@@ -62,8 +62,8 @@ _ublk_del_dev "${dev_id}"
rm -f "$HTLB_FILE"
umount "$HTLB_MNT"
rmdir "$HTLB_MNT"
echo "$OLD_NR_HP" > /proc/sys/vm/nr_hugepages
-_cleanup_test "shmem_zc"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_shmemzc_04.sh b/tools/testing/selftests/ublk/test_shmemzc_04.sh
index 899de088ece4..9a2a6c2e8abe 100755
--- a/tools/testing/selftests/ublk/test_shmemzc_04.sh
+++ b/tools/testing/selftests/ublk/test_shmemzc_04.sh
@@ -65,8 +65,8 @@ _ublk_del_dev "${dev_id}"
rm -f "$HTLB_FILE"
umount "$HTLB_MNT"
rmdir "$HTLB_MNT"
echo "$OLD_NR_HP" > /proc/sys/vm/nr_hugepages
-_cleanup_test "shmem_zc"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_01.sh b/tools/testing/selftests/ublk/test_stress_01.sh
index a9322ce496e9..f91783f27649 100755
--- a/tools/testing/selftests/ublk/test_stress_01.sh
+++ b/tools/testing/selftests/ublk/test_stress_01.sh
@@ -27,7 +27,7 @@ _create_backfile 2 128M
ublk_io_and_remove 8G -t null -q 4 &
ublk_io_and_remove 256M -t loop -q 4 "${UBLK_BACKFILES[0]}" &
ublk_io_and_remove 256M -t stripe -q 4 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_02.sh b/tools/testing/selftests/ublk/test_stress_02.sh
index 6c114194f9c9..b128d11658a8 100755
--- a/tools/testing/selftests/ublk/test_stress_02.sh
+++ b/tools/testing/selftests/ublk/test_stress_02.sh
@@ -29,7 +29,7 @@ for nr_queue in 1 4; do
ublk_io_and_kill_daemon 256M -t loop -q "$nr_queue" "${UBLK_BACKFILES[0]}" &
ublk_io_and_kill_daemon 256M -t stripe -q "$nr_queue" "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
done
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_03.sh b/tools/testing/selftests/ublk/test_stress_03.sh
index 4e81ca0db758..a0f0aba8eebc 100755
--- a/tools/testing/selftests/ublk/test_stress_03.sh
+++ b/tools/testing/selftests/ublk/test_stress_03.sh
@@ -47,7 +47,7 @@ if _have_feature "PER_IO_DAEMON"; then
ublk_io_and_remove 256M -t stripe -q 4 --auto_zc --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
ublk_io_and_remove 8G -t null -q 4 -z --auto_zc --auto_zc_fallback --nthreads 8 --per_io_tasks &
wait
fi
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_04.sh b/tools/testing/selftests/ublk/test_stress_04.sh
index 6c6f44b172bc..896eae68d444 100755
--- a/tools/testing/selftests/ublk/test_stress_04.sh
+++ b/tools/testing/selftests/ublk/test_stress_04.sh
@@ -46,7 +46,7 @@ if _have_feature "PER_IO_DAEMON"; then
ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback --nthreads 8 --per_io_tasks &
wait
fi
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_05.sh b/tools/testing/selftests/ublk/test_stress_05.sh
index 7e9324de2030..d6c00c72080d 100755
--- a/tools/testing/selftests/ublk/test_stress_05.sh
+++ b/tools/testing/selftests/ublk/test_stress_05.sh
@@ -77,7 +77,7 @@ if _have_feature "PER_IO_DAEMON"; then
ublk_io_and_remove 256M -t loop -q 4 --nthreads 8 --per_io_tasks -r 1 -i "$reissue" "${UBLK_BACKFILES[0]}" &
ublk_io_and_remove 8G -t null -q 4 --nthreads 8 --per_io_tasks -r 1 -i "$reissue" &
fi
wait
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_06.sh b/tools/testing/selftests/ublk/test_stress_06.sh
index c72e5d0b14be..9481a273a4b4 100755
--- a/tools/testing/selftests/ublk/test_stress_06.sh
+++ b/tools/testing/selftests/ublk/test_stress_06.sh
@@ -32,7 +32,7 @@ wait
ublk_io_and_remove 8G -t null -q 4 -u --nthreads 8 --per_io_tasks &
ublk_io_and_remove 256M -t loop -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[0]}" &
ublk_io_and_remove 256M -t stripe -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_07.sh b/tools/testing/selftests/ublk/test_stress_07.sh
index 04c2764d5238..3e01c037cffb 100755
--- a/tools/testing/selftests/ublk/test_stress_07.sh
+++ b/tools/testing/selftests/ublk/test_stress_07.sh
@@ -32,7 +32,7 @@ wait
ublk_io_and_kill_daemon 8G -t null -q 4 -u --nthreads 8 --per_io_tasks &
ublk_io_and_kill_daemon 256M -t loop -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[0]}" &
ublk_io_and_kill_daemon 256M -t stripe -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_08.sh b/tools/testing/selftests/ublk/test_stress_08.sh
index 37f7d204879a..5f32424d2892 100755
--- a/tools/testing/selftests/ublk/test_stress_08.sh
+++ b/tools/testing/selftests/ublk/test_stress_08.sh
@@ -38,7 +38,7 @@ ublk_io_and_remove 8G -t null -q 4 -b &
ublk_io_and_remove 256M -t loop -q 4 --auto_zc -b "${UBLK_BACKFILES[0]}" &
ublk_io_and_remove 256M -t stripe -q 4 --auto_zc -b "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
ublk_io_and_remove 8G -t null -q 4 -z --auto_zc --auto_zc_fallback -b &
wait
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stress_09.sh b/tools/testing/selftests/ublk/test_stress_09.sh
index 53c1e3b2ab30..64cb8d9b0438 100755
--- a/tools/testing/selftests/ublk/test_stress_09.sh
+++ b/tools/testing/selftests/ublk/test_stress_09.sh
@@ -37,7 +37,7 @@ ublk_io_and_kill_daemon 8G -t null -q 4 -z -b &
ublk_io_and_kill_daemon 256M -t loop -q 4 --auto_zc -b "${UBLK_BACKFILES[0]}" &
ublk_io_and_kill_daemon 256M -t stripe -q 4 -b "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback -b &
wait
-_cleanup_test "stress"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stripe_01.sh b/tools/testing/selftests/ublk/test_stripe_01.sh
index 3bc821aadad8..9ffce477b461 100755
--- a/tools/testing/selftests/ublk/test_stripe_01.sh
+++ b/tools/testing/selftests/ublk/test_stripe_01.sh
@@ -19,7 +19,7 @@ _check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=512M
ERR_CODE=$?
-_cleanup_test "stripe"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stripe_02.sh b/tools/testing/selftests/ublk/test_stripe_02.sh
index 4a7d2b21a6bf..4c172950a247 100755
--- a/tools/testing/selftests/ublk/test_stripe_02.sh
+++ b/tools/testing/selftests/ublk/test_stripe_02.sh
@@ -14,7 +14,7 @@ dev_id=$(_add_ublk_dev -t stripe "${UBLK_BACKFILES[0]}" "${UBLK_BACKFILES[1]}")
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
-_cleanup_test "stripe"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stripe_03.sh b/tools/testing/selftests/ublk/test_stripe_03.sh
index a1c159d54e53..2cdf9f958988 100755
--- a/tools/testing/selftests/ublk/test_stripe_03.sh
+++ b/tools/testing/selftests/ublk/test_stripe_03.sh
@@ -19,7 +19,7 @@ _check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=512M
ERR_CODE=$?
-_cleanup_test "stripe"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stripe_04.sh b/tools/testing/selftests/ublk/test_stripe_04.sh
index 0c30bd6c2b3b..e24120eaca0e 100755
--- a/tools/testing/selftests/ublk/test_stripe_04.sh
+++ b/tools/testing/selftests/ublk/test_stripe_04.sh
@@ -14,7 +14,7 @@ dev_id=$(_add_ublk_dev -t stripe -z -q 2 "${UBLK_BACKFILES[0]}" "${UBLK_BACKFILE
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
-_cleanup_test "stripe"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stripe_05.sh b/tools/testing/selftests/ublk/test_stripe_05.sh
index 6ddfa88ad226..f3de2d5cdfe4 100755
--- a/tools/testing/selftests/ublk/test_stripe_05.sh
+++ b/tools/testing/selftests/ublk/test_stripe_05.sh
@@ -19,7 +19,7 @@ _check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=512M
ERR_CODE=$?
-_cleanup_test "stripe"
+_cleanup_test
_show_result $TID $ERR_CODE
diff --git a/tools/testing/selftests/ublk/test_stripe_06.sh b/tools/testing/selftests/ublk/test_stripe_06.sh
index a2c7bf4cc613..3fd5cd902956 100755
--- a/tools/testing/selftests/ublk/test_stripe_06.sh
+++ b/tools/testing/selftests/ublk/test_stripe_06.sh
@@ -14,7 +14,7 @@ dev_id=$(_add_ublk_dev -t stripe -u -q 2 "${UBLK_BACKFILES[0]}" "${UBLK_BACKFILE
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
-_cleanup_test "stripe"
+_cleanup_test
_show_result $TID $ERR_CODE
--
2.45.2
^ permalink raw reply related
* [PATCH 2/3] selftests: ublk: enable test_integrity_02.sh on fio 3.42
From: Caleb Sander Mateos @ 2026-04-21 20:09 UTC (permalink / raw)
To: Ming Lei, Shuah Khan
Cc: linux-kernel, linux-block, linux-kselftest, Caleb Sander Mateos
In-Reply-To: <20260421200901.1528842-1-csander@purestorage.com>
fio 3.42 was released with the needed fix for test_integrity_02.sh.
Allow 3.42 and newer in the fio version check.
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
tools/testing/selftests/ublk/test_integrity_02.sh | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/ublk/test_integrity_02.sh b/tools/testing/selftests/ublk/test_integrity_02.sh
index aaf1f52da559..2c35fbc8a7cc 100755
--- a/tools/testing/selftests/ublk/test_integrity_02.sh
+++ b/tools/testing/selftests/ublk/test_integrity_02.sh
@@ -5,13 +5,14 @@
if ! _have_program fio; then
exit $UBLK_SKIP_CODE
fi
+min_fio_version=fio-3.42
fio_version=$(fio --version)
-if [[ "$fio_version" =~ fio-[0-9]+\.[0-9]+$ ]]; then
- echo "Requires development fio version with https://github.com/axboe/fio/pull/1992"
+if ! sort --version-sort --check=quiet <(printf "%s\n%s\n" "$min_fio_version" "$fio_version"); then
+ echo "Requires fio version with https://github.com/axboe/fio/pull/1992"
exit $UBLK_SKIP_CODE
fi
ERR_CODE=0
--
2.45.2
^ permalink raw reply related
* [PATCH 0/3] selftests: ublk: integrity test cleanups
From: Caleb Sander Mateos @ 2026-04-21 20:08 UTC (permalink / raw)
To: Ming Lei, Shuah Khan
Cc: linux-kernel, linux-block, linux-kselftest, Caleb Sander Mateos
Update test_integrity_02 to allow fio-3.42 now that it's been released.
Add a modified version test_integrity_03 that uses auto integrity and
avoids the newer fio requirement.
Also clean up the callers of _cleanup passing an unnecessary argument.
Caleb Sander Mateos (3):
selftests: ublk: remove unused argument to _cleanup
selftests: ublk: enable test_integrity_02.sh on fio 3.42
selftests: ublk: add ublk auto integrity test
tools/testing/selftests/ublk/Makefile | 1 +
tools/testing/selftests/ublk/test_batch_01.sh | 4 +-
tools/testing/selftests/ublk/test_batch_02.sh | 2 +-
tools/testing/selftests/ublk/test_batch_03.sh | 2 +-
.../testing/selftests/ublk/test_generic_02.sh | 4 +-
.../testing/selftests/ublk/test_generic_03.sh | 2 +-
.../testing/selftests/ublk/test_generic_06.sh | 2 +-
.../testing/selftests/ublk/test_generic_07.sh | 2 +-
.../testing/selftests/ublk/test_generic_08.sh | 4 +-
.../testing/selftests/ublk/test_generic_09.sh | 2 +-
.../testing/selftests/ublk/test_generic_10.sh | 2 +-
.../testing/selftests/ublk/test_generic_12.sh | 4 +-
.../testing/selftests/ublk/test_generic_13.sh | 2 +-
.../testing/selftests/ublk/test_generic_16.sh | 4 +-
.../testing/selftests/ublk/test_generic_17.sh | 2 +-
.../selftests/ublk/test_integrity_02.sh | 5 +-
.../selftests/ublk/test_integrity_03.sh | 103 ++++++++++++++++++
tools/testing/selftests/ublk/test_loop_01.sh | 2 +-
tools/testing/selftests/ublk/test_loop_02.sh | 2 +-
tools/testing/selftests/ublk/test_loop_03.sh | 2 +-
tools/testing/selftests/ublk/test_loop_04.sh | 2 +-
tools/testing/selftests/ublk/test_loop_05.sh | 2 +-
tools/testing/selftests/ublk/test_loop_06.sh | 2 +-
tools/testing/selftests/ublk/test_loop_07.sh | 2 +-
tools/testing/selftests/ublk/test_null_01.sh | 2 +-
tools/testing/selftests/ublk/test_null_02.sh | 2 +-
tools/testing/selftests/ublk/test_null_03.sh | 2 +-
tools/testing/selftests/ublk/test_part_01.sh | 4 +-
tools/testing/selftests/ublk/test_part_02.sh | 2 +-
.../testing/selftests/ublk/test_recover_01.sh | 2 +-
.../testing/selftests/ublk/test_recover_02.sh | 2 +-
.../testing/selftests/ublk/test_recover_03.sh | 2 +-
.../testing/selftests/ublk/test_recover_04.sh | 2 +-
.../testing/selftests/ublk/test_shmemzc_01.sh | 2 +-
.../testing/selftests/ublk/test_shmemzc_02.sh | 2 +-
.../testing/selftests/ublk/test_shmemzc_03.sh | 2 +-
.../testing/selftests/ublk/test_shmemzc_04.sh | 2 +-
.../testing/selftests/ublk/test_stress_01.sh | 2 +-
.../testing/selftests/ublk/test_stress_02.sh | 2 +-
.../testing/selftests/ublk/test_stress_03.sh | 2 +-
.../testing/selftests/ublk/test_stress_04.sh | 2 +-
.../testing/selftests/ublk/test_stress_05.sh | 2 +-
.../testing/selftests/ublk/test_stress_06.sh | 2 +-
.../testing/selftests/ublk/test_stress_07.sh | 2 +-
.../testing/selftests/ublk/test_stress_08.sh | 2 +-
.../testing/selftests/ublk/test_stress_09.sh | 2 +-
.../testing/selftests/ublk/test_stripe_01.sh | 2 +-
.../testing/selftests/ublk/test_stripe_02.sh | 2 +-
.../testing/selftests/ublk/test_stripe_03.sh | 2 +-
.../testing/selftests/ublk/test_stripe_04.sh | 2 +-
.../testing/selftests/ublk/test_stripe_05.sh | 2 +-
.../testing/selftests/ublk/test_stripe_06.sh | 2 +-
52 files changed, 162 insertions(+), 57 deletions(-)
create mode 100755 tools/testing/selftests/ublk/test_integrity_03.sh
--
2.45.2
^ permalink raw reply
* [PATCH] ublk: optimize ublk_rq_has_data()
From: Caleb Sander Mateos @ 2026-04-21 19:47 UTC (permalink / raw)
To: Ming Lei, Jens Axboe; +Cc: Caleb Sander Mateos, linux-block, linux-kernel
ublk_rq_has_data() currently uses bio_has_data(), which involves 2
indirections and several branches. Use blk_rq_bytes() instead, which
performs a single indirection with no branches.
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
drivers/block/ublk_drv.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index ef8a0705e68b..5f9b3c3876f4 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -1172,11 +1172,11 @@ static inline struct ublk_queue *ublk_get_queue(struct ublk_device *dev,
return dev->queues[qid];
}
static inline bool ublk_rq_has_data(const struct request *rq)
{
- return bio_has_data(rq->bio);
+ return blk_rq_bytes(rq);
}
static inline struct ublksrv_io_desc *
ublk_queue_cmd_buf(struct ublk_device *ub, int q_id)
{
--
2.45.2
^ permalink raw reply related
* Re: [RFC PATCH v2 2/4] mm/zsmalloc: introduce zs_free_deferred() for async handle freeing
From: Nhat Pham @ 2026-04-21 19:46 UTC (permalink / raw)
To: Wenchao Hao
Cc: Andrew Morton, Chengming Zhou, Jens Axboe, Johannes Weiner,
Minchan Kim, Sergey Senozhatsky, Yosry Ahmed, linux-block,
linux-kernel, linux-mm, Barry Song, Xueyuan Chen, Wenchao Hao
In-Reply-To: <20260421121616.3298845-3-haowenchao@xiaomi.com>
On Tue, Apr 21, 2026 at 5:16 AM Wenchao Hao <haowenchao22@gmail.com> wrote:
>
> zs_free() is expensive due to internal locking (pool->lock, class->lock)
> and potential zspage freeing. On the process exit path, the slow
> zs_free() blocks memory reclamation, delaying overall memory release.
> This has been reported to significantly impact Android low-memory
> killing where slot_free() accounts for over 80% of the total swap
> entry freeing cost.
>
> Introduce zs_free_deferred() which queues handles into a fixed-size
> per-pool array for later processing by a workqueue. This allows callers
> to defer the expensive zs_free() and return quickly, so the process
> exit path can release memory faster. The array capacity is derived from
> a 128MB uncompressed data budget (128MB >> PAGE_SHIFT entries), which
> scales naturally with PAGE_SIZE. When the array reaches half capacity,
> the workqueue is scheduled to drain pending handles.
>
> zs_free_deferred() uses spin_trylock() to access the deferred queue.
> If the lock is contended (e.g. drain in progress) or the queue is full,
> it falls back to synchronous zs_free() to guarantee correctness.
>
> Also introduce zs_free_deferred_flush() for use during pool teardown to
> ensure all pending handles are freed.
Hmmm per-pool workqueue.
Does that mean that if you only have one zs pool (in the case of
zswap, or if you only have one zram device), you'll have less
concurrency in freeing up zsmalloc memory for process teardown? Would
this be problematic?
I think Kairui was also suggesting per-cpu-fying these batches/queues.
>
> Signed-off-by: Wenchao Hao <haowenchao@xiaomi.com>
> ---
> include/linux/zsmalloc.h | 2 +
> mm/zsmalloc.c | 111 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 113 insertions(+)
>
> diff --git a/include/linux/zsmalloc.h b/include/linux/zsmalloc.h
> index 478410c880b1..1e5ac1a39d41 100644
> --- a/include/linux/zsmalloc.h
> +++ b/include/linux/zsmalloc.h
> @@ -30,6 +30,8 @@ void zs_destroy_pool(struct zs_pool *pool);
> unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t flags,
> const int nid);
> void zs_free(struct zs_pool *pool, unsigned long obj);
> +void zs_free_deferred(struct zs_pool *pool, unsigned long handle);
> +void zs_free_deferred_flush(struct zs_pool *pool);
>
> size_t zs_huge_class_size(struct zs_pool *pool);
>
> diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
> index 40687c8a7469..defc892555e4 100644
> --- a/mm/zsmalloc.c
> +++ b/mm/zsmalloc.c
> @@ -53,6 +53,10 @@
>
> #define ZS_HANDLE_SIZE (sizeof(unsigned long))
>
> +#define ZS_DEFERRED_FREE_MAX_BYTES (128 << 20)
> +#define ZS_DEFERRED_FREE_CAPACITY (ZS_DEFERRED_FREE_MAX_BYTES >> PAGE_SHIFT)
> +#define ZS_DEFERRED_FREE_THRESHOLD (ZS_DEFERRED_FREE_CAPACITY / 2)
> +
> /*
> * Object location (<PFN>, <obj_idx>) is encoded as
> * a single (unsigned long) handle value.
> @@ -217,6 +221,13 @@ struct zs_pool {
> /* protect zspage migration/compaction */
> rwlock_t lock;
> atomic_t compaction_in_progress;
> +
> + /* deferred free support */
> + spinlock_t deferred_lock;
> + unsigned long *deferred_handles;
> + unsigned int deferred_count;
> + unsigned int deferred_capacity;
> + struct work_struct deferred_free_work;
> };
>
> static inline void zpdesc_set_first(struct zpdesc *zpdesc)
> @@ -579,6 +590,19 @@ static int zs_stats_size_show(struct seq_file *s, void *v)
> }
> DEFINE_SHOW_ATTRIBUTE(zs_stats_size);
>
> +static int zs_stats_deferred_show(struct seq_file *s, void *v)
> +{
> + struct zs_pool *pool = s->private;
> +
> + spin_lock(&pool->deferred_lock);
> + seq_printf(s, "pending: %u\n", pool->deferred_count);
> + seq_printf(s, "capacity: %u\n", pool->deferred_capacity);
> + spin_unlock(&pool->deferred_lock);
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(zs_stats_deferred);
> +
> static void zs_pool_stat_create(struct zs_pool *pool, const char *name)
> {
> if (!zs_stat_root) {
> @@ -590,6 +614,9 @@ static void zs_pool_stat_create(struct zs_pool *pool, const char *name)
>
> debugfs_create_file("classes", S_IFREG | 0444, pool->stat_dentry, pool,
> &zs_stats_size_fops);
> + debugfs_create_file("deferred_free", S_IFREG | 0444,
> + pool->stat_dentry, pool,
> + &zs_stats_deferred_fops);
> }
>
> static void zs_pool_stat_destroy(struct zs_pool *pool)
> @@ -1432,6 +1459,76 @@ void zs_free(struct zs_pool *pool, unsigned long handle)
> }
> EXPORT_SYMBOL_GPL(zs_free);
>
> +static void zs_deferred_free_work(struct work_struct *work)
> +{
> + struct zs_pool *pool = container_of(work, struct zs_pool,
> + deferred_free_work);
> + unsigned long handle;
> +
> + while (1) {
> + spin_lock(&pool->deferred_lock);
> + if (pool->deferred_count == 0) {
> + spin_unlock(&pool->deferred_lock);
> + break;
> + }
> + handle = pool->deferred_handles[--pool->deferred_count];
> + spin_unlock(&pool->deferred_lock);
Any reason why we're locking, grabbing a handle, then unlocking, one
at a time? Why dont we just lock, grab all the handles (or at least a
batch of them), unlock, then process the handles one at a time?
We can also have a pair of handle arrays. Whenever defer worker is
woken up, just swap the arrays under the lock, then free the handles
in the old array :)
> +
> + zs_free(pool, handle);
> + cond_resched();
> + }
> +}
> +
> +/**
> + * zs_free_deferred - queue a handle for asynchronous freeing
> + * @pool: pool to free from
> + * @handle: handle to free
> + *
> + * Place @handle into a deferred free queue for later processing by a
> + * workqueue. This is intended for callers that are in atomic context
> + * (e.g. under a spinlock) and cannot afford the cost of zs_free()
> + * directly. When the queue reaches a threshold the work is scheduled.
> + * Falls back to synchronous zs_free() if the lock is contended (drain
> + * in progress) or if the queue is full.
> + */
> +void zs_free_deferred(struct zs_pool *pool, unsigned long handle)
> +{
> + if (IS_ERR_OR_NULL((void *)handle))
> + return;
> +
> + if (!spin_trylock(&pool->deferred_lock))
> + goto sync_free;
> +
> + if (pool->deferred_count >= pool->deferred_capacity) {
> + spin_unlock(&pool->deferred_lock);
> + goto sync_free;
> + }
> +
> + pool->deferred_handles[pool->deferred_count++] = handle;
> + if (pool->deferred_count >= ZS_DEFERRED_FREE_THRESHOLD)
> + queue_work(system_wq, &pool->deferred_free_work);
> + spin_unlock(&pool->deferred_lock);
> + return;
> +
> +sync_free:
> + zs_free(pool, handle);
> +}
> +EXPORT_SYMBOL_GPL(zs_free_deferred);
> +
> +/**
> + * zs_free_deferred_flush - flush all pending deferred frees
> + * @pool: pool to flush
> + *
> + * Wait for any scheduled work to complete, then drain any remaining
> + * handles. Must be called from process context.
> + */
> +void zs_free_deferred_flush(struct zs_pool *pool)
> +{
> + flush_work(&pool->deferred_free_work);
> + zs_deferred_free_work(&pool->deferred_free_work);
> +}
> +EXPORT_SYMBOL_GPL(zs_free_deferred_flush);
> +
> static void zs_object_copy(struct size_class *class, unsigned long dst,
> unsigned long src)
> {
> @@ -2099,6 +2196,18 @@ struct zs_pool *zs_create_pool(const char *name)
> rwlock_init(&pool->lock);
> atomic_set(&pool->compaction_in_progress, 0);
>
> + spin_lock_init(&pool->deferred_lock);
> + pool->deferred_capacity = ZS_DEFERRED_FREE_CAPACITY;
> + pool->deferred_handles = kvmalloc_array(pool->deferred_capacity,
> + sizeof(unsigned long),
> + GFP_KERNEL);
> + if (!pool->deferred_handles) {
> + kfree(pool);
> + return NULL;
> + }
> + pool->deferred_count = 0;
> + INIT_WORK(&pool->deferred_free_work, zs_deferred_free_work);
> +
> pool->name = kstrdup(name, GFP_KERNEL);
> if (!pool->name)
> goto err;
> @@ -2201,6 +2310,7 @@ void zs_destroy_pool(struct zs_pool *pool)
> int i;
>
> zs_unregister_shrinker(pool);
> + zs_free_deferred_flush(pool);
> zs_flush_migration(pool);
> zs_pool_stat_destroy(pool);
>
> @@ -2224,6 +2334,7 @@ void zs_destroy_pool(struct zs_pool *pool)
> kfree(class);
> }
>
> + kvfree(pool->deferred_handles);
> kfree(pool->name);
> kfree(pool);
> }
> --
> 2.34.1
>
^ permalink raw reply
* Re: [RFC PATCH v2 0/4] mm/zsmalloc: reduce zs_free() latency on swap release path
From: Nhat Pham @ 2026-04-21 18:25 UTC (permalink / raw)
To: Kairui Song
Cc: Wenchao Hao, Andrew Morton, Chengming Zhou, Jens Axboe,
Johannes Weiner, Minchan Kim, Sergey Senozhatsky, Yosry Ahmed,
linux-block, linux-kernel, linux-mm, Barry Song, Xueyuan Chen,
Wenchao Hao
In-Reply-To: <CAKEwX=PkFiP+u+ThrzjTKBi+usQf2uuhTZcfB2BNNA8RboOFDQ@mail.gmail.com>
On Tue, Apr 21, 2026 at 11:07 AM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Tue, Apr 21, 2026 at 10:18 AM Kairui Song <ryncsn@gmail.com> wrote:
> >
> > On Tue, Apr 21, 2026 at 11:55 PM Nhat Pham <nphamcs@gmail.com> wrote:
> > >
> >
> > Thanks for adding me to the Cc list :), Barry started this idea with
> > ZRAM, which looks very interesting to me.
> >
> > > On Tue, Apr 21, 2026 at 5:16 AM 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.
> > > >
> > > > When anon folios and swap entries are mixed within a
> > > > process, reclaiming anon folios from killed processes
> > > > helps return memory to the system as quickly as possible,
> > > > so that newly launched applications can satisfy their
> > > > memory demands. It is not ideal for swap freeing to block
> > > > anon folio freeing. On the other hand, swap freeing can
> > > > still return memory to the system, although at a slower
> > > > rate due to memory compression.
> > >
> > > Is this correct? I don't think we do decompression in
> > > zswap_invalidate() path. We do decompression in zswap_load(), but as a
> > > separate step from zswap_invalidate().
> >
> > It's not about decompression. I think what Wenchao means here is that:
> > freeing the swap entry also releases the backing compression data, but
> > compared to freeing an actual folio (which bring back a free folio to
> > reduce memory pressure), you may need to free a lot of swap entries to
> > free one whole folio, because the compressed data could be much
> > smaller than folio and with fragmentation. And swap entry freeing is
> > still not fast enough to be ignored.
>
> Ah I see yeah. That's the not "as much bang-for-your-buck" as folio
> freeing category. I agree on this point.
>
> >
> > >
> > > zswap/zsmalloc entry freeing is decoupled from decompression. For
> > > example, on process teardown, we free the zsmalloc memory but never
> > > decompress (if we do then it's a bug to be fixed lol, but I doubt it).
> > >
> > > Zsmalloc freeing might not be worth as much bang-for-your-buck wise
> > > compared to anon folio freeing, but if it's "expensive", then I think
> > > that points to a different root-cause: zsmalloc's poor scalability in
> > > the free path.
> >
> > That's a very nice insight. I had an idea previously that can we have
> > something like a zs free bulk? Freeing handles one by one does seem
> > expensive.
> > https://lore.kernel.org/linux-mm/adt3Q_SRToF6fb3W@KASONG-MC4/
> >
> > It might be tricky to do so though.
> >
> > It will be best if we can speed up everything, doing things async
> > doesn't reduce the total amount of work, and might cause more trouble
> > like worker overhead or delayed freeing causing more memory pressure,
> > if the workqueue didn't run in time. Or maybe a process is almost
> > completely swapped out, then this won't help at all.
> >
> > I'm not against the async idea, they might combine well.
>
> Completely agree! I was thinking about batching the free operations
> for zsmalloc. Right now seems like even if we have a contiguous range
> of swap slots to be freed, we call one
> zram_slot_free_notify/zswap_invalidate at a time, which then call
> zs_free one at a time? I wonder if there's any batching opportunity
> here. Might be complicated with the pool lock and class lock dance in
> zs_free() though :)
>
> And yeah the async stuff is orthogonal too.
>
> >
> > >
> > > I've stared at this code path for a bit, because my other patch series
> > > (vswap - see [1]) was reported to display regression on the free path
> > > on the usemem benchmark. And one of the issues was the contention
> > > between compaction (both systemwide compaction, i.e zs_page_migrate,
> > > and zsmalloc's internal compaction, but mostly the former).:
> > >
> > > * zs_free read-acquires pool->lock, and compaction write-acquires the
> > > same lock. So the compaction thread will make all zs free-ers wait for
> > > it. I saw this read lock delay when I perfed the free step of usemem.
> > >
> > > * If this lock has fair queue-ing semantics (I have not checked), then
> > > if there a compaction is behind a bunch of zs_free in the queue, then
> > > all the subsequent zs_free's ers are blocked :)
> > >
> > > * I'm also curious about cache-friendliness of this rwlock, bouncing
> > > across CPUs, if you have multiple processes being torn down
> > > concurrently.
> >
> > That's interesting, when I mentioned zs free bulk I was thinking that,
> > if we have a percpu queue, at least we may try read lock that on every
> > enqueue, free the whole queue if successful, then release the lock.
> > I'm sure there are more ways to optimize that, just a random idea :)
>
> Yep! Would be nice to have some perf trace to pinpoint where the overhead is.
>
Ah OK - I found this thread now:
https://lore.kernel.org/linux-mm/20260414054930.225853-1-xueyuan.chen21@gmail.com/
Hmm, free_zspage() and kmem_cache_free().
* kmem_cache_free() is just handle freeing. Bulk-freeing?
* free_zspage() looks like just ordinary teardown work :( Seems like
we're not spinning any lock here - we just try lock the backing pages,
and the rest is normal work. Not sure how to optimize this - perhaps
deferring is the only way.
^ permalink raw reply
* Re: [RFC PATCH v2 0/4] mm/zsmalloc: reduce zs_free() latency on swap release path
From: Nhat Pham @ 2026-04-21 18:07 UTC (permalink / raw)
To: Kairui Song
Cc: Wenchao Hao, Andrew Morton, Chengming Zhou, Jens Axboe,
Johannes Weiner, Minchan Kim, Sergey Senozhatsky, Yosry Ahmed,
linux-block, linux-kernel, linux-mm, Barry Song, Xueyuan Chen,
Wenchao Hao
In-Reply-To: <CAMgjq7Ae_VH6mLQito5MFnxmp7ScZvF9xw4P-yU-yUBkPZ9_YQ@mail.gmail.com>
On Tue, Apr 21, 2026 at 10:18 AM Kairui Song <ryncsn@gmail.com> wrote:
>
> On Tue, Apr 21, 2026 at 11:55 PM Nhat Pham <nphamcs@gmail.com> wrote:
> >
>
> Thanks for adding me to the Cc list :), Barry started this idea with
> ZRAM, which looks very interesting to me.
>
> > On Tue, Apr 21, 2026 at 5:16 AM 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.
> > >
> > > When anon folios and swap entries are mixed within a
> > > process, reclaiming anon folios from killed processes
> > > helps return memory to the system as quickly as possible,
> > > so that newly launched applications can satisfy their
> > > memory demands. It is not ideal for swap freeing to block
> > > anon folio freeing. On the other hand, swap freeing can
> > > still return memory to the system, although at a slower
> > > rate due to memory compression.
> >
> > Is this correct? I don't think we do decompression in
> > zswap_invalidate() path. We do decompression in zswap_load(), but as a
> > separate step from zswap_invalidate().
>
> It's not about decompression. I think what Wenchao means here is that:
> freeing the swap entry also releases the backing compression data, but
> compared to freeing an actual folio (which bring back a free folio to
> reduce memory pressure), you may need to free a lot of swap entries to
> free one whole folio, because the compressed data could be much
> smaller than folio and with fragmentation. And swap entry freeing is
> still not fast enough to be ignored.
Ah I see yeah. That's the not "as much bang-for-your-buck" as folio
freeing category. I agree on this point.
>
> >
> > zswap/zsmalloc entry freeing is decoupled from decompression. For
> > example, on process teardown, we free the zsmalloc memory but never
> > decompress (if we do then it's a bug to be fixed lol, but I doubt it).
> >
> > Zsmalloc freeing might not be worth as much bang-for-your-buck wise
> > compared to anon folio freeing, but if it's "expensive", then I think
> > that points to a different root-cause: zsmalloc's poor scalability in
> > the free path.
>
> That's a very nice insight. I had an idea previously that can we have
> something like a zs free bulk? Freeing handles one by one does seem
> expensive.
> https://lore.kernel.org/linux-mm/adt3Q_SRToF6fb3W@KASONG-MC4/
>
> It might be tricky to do so though.
>
> It will be best if we can speed up everything, doing things async
> doesn't reduce the total amount of work, and might cause more trouble
> like worker overhead or delayed freeing causing more memory pressure,
> if the workqueue didn't run in time. Or maybe a process is almost
> completely swapped out, then this won't help at all.
>
> I'm not against the async idea, they might combine well.
Completely agree! I was thinking about batching the free operations
for zsmalloc. Right now seems like even if we have a contiguous range
of swap slots to be freed, we call one
zram_slot_free_notify/zswap_invalidate at a time, which then call
zs_free one at a time? I wonder if there's any batching opportunity
here. Might be complicated with the pool lock and class lock dance in
zs_free() though :)
And yeah the async stuff is orthogonal too.
>
> >
> > I've stared at this code path for a bit, because my other patch series
> > (vswap - see [1]) was reported to display regression on the free path
> > on the usemem benchmark. And one of the issues was the contention
> > between compaction (both systemwide compaction, i.e zs_page_migrate,
> > and zsmalloc's internal compaction, but mostly the former).:
> >
> > * zs_free read-acquires pool->lock, and compaction write-acquires the
> > same lock. So the compaction thread will make all zs free-ers wait for
> > it. I saw this read lock delay when I perfed the free step of usemem.
> >
> > * If this lock has fair queue-ing semantics (I have not checked), then
> > if there a compaction is behind a bunch of zs_free in the queue, then
> > all the subsequent zs_free's ers are blocked :)
> >
> > * I'm also curious about cache-friendliness of this rwlock, bouncing
> > across CPUs, if you have multiple processes being torn down
> > concurrently.
>
> That's interesting, when I mentioned zs free bulk I was thinking that,
> if we have a percpu queue, at least we may try read lock that on every
> enqueue, free the whole queue if successful, then release the lock.
> I'm sure there are more ways to optimize that, just a random idea :)
Yep! Would be nice to have some perf trace to pinpoint where the overhead is.
On my end, I perfed the free phase of usemem. It varies a bit based on
exact build config, kernel version, or even between runs, but the
cheapest I've seen for the pool lock contention overhead is about 3%
of the free phase (this is on baseline, not vswap kernel). That's
pretty big (bigger than vswap overhead even on the kernels with vswap,
which is kinda silly). Obviously the host was very overcommitted, so
compaction was running in the background at the same time, but
still...
^ permalink raw reply
* Re: RCU warning off ublk_buf_cleanup() -> mas_for_each()
From: Jens Axboe @ 2026-04-21 18:04 UTC (permalink / raw)
To: Ming Lei; +Cc: io-uring, linux-block@vger.kernel.org, Liam R. Howlett
In-Reply-To: <0349d72d-dff8-4f9f-b448-919fa5ae96da@kernel.dk>
On 4/21/26 11:47 AM, Jens Axboe wrote:
> Hi Ming,
>
> Ran into the below running tests on the current tree:
>
> =============================
> WARNING: suspicious RCU usage
> 7.0.0+ #16 Tainted: G N
> -----------------------------
> lib/maple_tree.c:759 suspicious rcu_dereference_check() usage!
FWIW, here's what claude spits out on the maple tree usage for
ublk. Simply passing it on...
Issue 1: ublk_buf_cleanup — missing RCU/lock for mas_for_each (line 5489)
ublk_buf_cleanup iterates the tree using mas_for_each without holding either
rcu_read_lock or mas_lock. The mas_find() documentation explicitly states: "Must
hold rcu_read_lock or the write lock." Internally, mas_find → mas_next_slot →
mt_slot → rcu_dereference_check(slots[offset], mt_locked(mt)). With neither RCU nor
the tree lock held, this will fire a lockdep splat on CONFIG_PROVE_RCU=y kernels.
While functionally safe (exclusive access during device release), the API contract
is violated. Fix: wrap the iteration in rcu_read_lock/unlock, or use
mas_lock/unlock.
Issue 2: __ublk_ctrl_unreg_buf — unpin_user_pages under spinlock (line 5431-5455)
This function holds mas_lock (a spinlock — atomic context) while unpinning
potentially many pages in a loop. For a large registered buffer with many disjoint
PFN ranges, this holds the spinlock for an extended period. unpin_user_pages →
gup_put_folio → folio_put could also grab additional locks if the folio's refcount
drops to zero.
A cleaner pattern would be to collect entries, drop mas_lock, then unpin. Compare
with ublk_buf_cleanup which does the same unpinning work outside any lock — the
asymmetry is notable.
Issue 3: ublk_buf_cleanup — kfree without mas_erase (line 5504)
The cleanup function does kfree(range) during iteration without first calling
mas_erase(). This leaves dangling pointers in the tree nodes until mtree_destroy is
called on line 5506. Not a bug (no concurrent access, mtree_destroy doesn't
dereference stored entries), but it's inconsistent with ublk_buf_erase_ranges and
__ublk_ctrl_unreg_buf which both properly erase before freeing.
--
Jens Axboe
^ permalink raw reply
* RCU warning off ublk_buf_cleanup() -> mas_for_each()
From: Jens Axboe @ 2026-04-21 17:47 UTC (permalink / raw)
To: Ming Lei; +Cc: io-uring, linux-block@vger.kernel.org, Liam R. Howlett
Hi Ming,
Ran into the below running tests on the current tree:
=============================
WARNING: suspicious RCU usage
7.0.0+ #16 Tainted: G N
-----------------------------
lib/maple_tree.c:759 suspicious rcu_dereference_check() usage!
other info that might help us debug this:
rcu_scheduler_active = 2, debug_locks = 1
1 lock held by iou-wrk-55535/55536:
#0: ffff800085a451a0 (ublk_ctl_mutex){+.+.}-{4:4}, at: ublk_ctrl_del_dev+0xdc/0x2f8
stack backtrace:
CPU: 4 UID: 0 PID: 55536 Comm: iou-wrk-55535 Tainted: G N 7.0.0+ #16 PREEMPT
Tainted: [N]=TEST
Hardware name: linux,dummy-virt (DT)
Call trace:
show_stack+0x1c/0x30 (C)
dump_stack_lvl+0x68/0x90
dump_stack+0x18/0x20
lockdep_rcu_suspicious+0x170/0x200
mas_walk+0x3f0/0x6a0
mas_find+0x1b4/0x6b0
ublk_buf_cleanup+0xe0/0x240
ublk_cdev_rel+0x34/0x1b0
device_release+0xa4/0x350
kobject_put+0x138/0x250
put_device+0x18/0x30
ublk_put_device+0x18/0x28
ublk_ctrl_del_dev+0x120/0x2f8
ublk_ctrl_uring_cmd+0x598/0x29b8
io_uring_cmd+0x1e0/0x468
__io_issue_sqe+0xa4/0x748
io_issue_sqe+0x80/0xf68
io_wq_submit_work+0x26c/0xdc8
io_worker_handle_work+0x334/0xf20
io_wq_worker+0x278/0x9e8
ret_from_fork+0x10/0x20
Buffer I/O error on dev ublkb0, logical block 0, async page read
Buffer I/O error on dev ublkb0, logical block 0, async page read
ublkb0: unable to read partition table
Buffer I/O error on dev ublkb0, logical block 0, async page read
Buffer I/O error on dev ublkb0, logical block 0, async page read
Buffer I/O error on dev ublkb0, logical block 512, async page read
Buffer I/O error on dev ublkb0, logical block 512, async page read
Buffer I/O error on dev ublkb0, logical block 0, async page read
Buffer I/O error on dev ublkb0, logical block 512, async page read
and I briefly looked at it, but then just gave up as a) the maple tree
documentation is not that detailed, and b) other in-tree users also just
call mas_for_each() without either a lock held or RCU read side locked.
Adding Liam for shedding some light on this...
--
Jens Axboe
^ permalink raw reply
* Re: (subset) [PATCH 1/2] sg: don't use GFP_ATOMIC in sg_start_req
From: Jens Axboe @ 2026-04-21 17:42 UTC (permalink / raw)
To: Doug Gilbert, Martin K. Petersen, Christoph Hellwig
Cc: Shin'ichiro Kawasaki, linux-block, linux-scsi
In-Reply-To: <20260415060813.807659-2-hch@lst.de>
On Wed, 15 Apr 2026 08:08:06 +0200, Christoph Hellwig wrote:
> sg_start_req is called from normal user context and can sleep when
> waiting for memory. Switch it to use GFP_KERNEL, which fixes allocation
> failures seend with the bio_alloc rework.
Applied, thanks!
[2/2] block: only restrict bio allocation gfp mask asked to block
commit: b5129bda5bbcceea5b2589c8248d39f77660aa19
Best regards,
--
Jens Axboe
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox