* [PATCH 1/2] crypto: qat - remove unused character device and IOCTLs
From: Giovanni Cabiddu @ 2026-05-08 9:18 UTC (permalink / raw)
To: herbert
Cc: linux-crypto, qat-linux, wangzhi, byu, w15303746062, vdronov,
Giovanni Cabiddu, stable, Ahsan Atta
In-Reply-To: <20260508091912.206913-1-giovanni.cabiddu@intel.com>
The QAT driver exposes a character device (qat_adf_ctl) with IOCTLs
for device configuration, start, stop, status query and enumeration.
These IOCTLs are not part of any public uAPI header and have no known
in-tree or out-of-tree users. Device lifecycle is already managed via
sysfs.
The ioctl interface also increases the attack surface and is the
subject of a number of bug reports.
Remove the character device, the IOCTL definitions, and the related
data structures (adf_dev_status_info, adf_user_cfg_key_val,
adf_user_cfg_section, adf_user_cfg_ctl_data). Drop the now-unused
adf_cfg_user.h header and strip adf_ctl_drv.c down to the minimal
module_init/module_exit hooks for workqueue, AER, and crypto/compression
algorithm registration.
Additionally, drop the entry associated to QAT IOCTLs in
ioctl-number.rst.
Cc: stable@vger.kernel.org
Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework")
Reported-by: Zhi Wang <wangzhi@stu.xidian.edu.cn>
Reported-by: Bin Yu <byu@xidian.edu.cn>
Reported-by: MingYu Wang <w15303746062@163.com>
Closes: https://lore.kernel.org/all/61d6d499.ab89.19b9b7f3186.Coremail.wangzhi_xd@stu.xidian.edu.cn/
Link: https://lore.kernel.org/all/20260508034841.256794-1-w15303746062@163.com/
Link: https://lore.kernel.org/all/20260508023542.256299-1-w15303746062@163.com/
Link: https://lore.kernel.org/all/20260504025120.98242-1-w15303746062@163.com/
Signed-off-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
Reviewed-by: Ahsan Atta <ahsan.atta@intel.com>
---
.../userspace-api/ioctl/ioctl-number.rst | 1 -
.../intel/qat/qat_common/adf_cfg_common.h | 27 --
.../intel/qat/qat_common/adf_cfg_user.h | 38 --
.../crypto/intel/qat/qat_common/adf_ctl_drv.c | 404 +-----------------
4 files changed, 1 insertion(+), 469 deletions(-)
delete mode 100644 drivers/crypto/intel/qat/qat_common/adf_cfg_user.h
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 331223761fff..29a08bc059dd 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -229,7 +229,6 @@ Code Seq# Include File Comments
<mailto:gregkh@linuxfoundation.org>
'a' all linux/atm*.h, linux/sonet.h ATM on linux
<http://lrcwww.epfl.ch/>
-'a' 00-0F drivers/crypto/qat/qat_common/adf_cfg_common.h conflict! qat driver
'b' 00-FF conflict! bit3 vme host bridge
<mailto:natalia@nikhefk.nikhef.nl>
'b' 00-0F linux/dma-buf.h conflict!
diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h
index 81e9e9d7eccd..88afca32fbe8 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h
+++ b/drivers/crypto/intel/qat/qat_common/adf_cfg_common.h
@@ -4,7 +4,6 @@
#define ADF_CFG_COMMON_H_
#include <linux/types.h>
-#include <linux/ioctl.h>
#define ADF_CFG_MAX_STR_LEN 64
#define ADF_CFG_MAX_KEY_LEN_IN_BYTES ADF_CFG_MAX_STR_LEN
@@ -15,7 +14,6 @@
#define ADF_CFG_ALL_DEVICES 0xFE
#define ADF_CFG_NO_DEVICE 0xFF
#define ADF_CFG_AFFINITY_WHATEVER 0xFF
-#define MAX_DEVICE_NAME_SIZE 32
#define ADF_MAX_DEVICES (32 * 32)
#define ADF_DEVS_ARRAY_SIZE BITS_TO_LONGS(ADF_MAX_DEVICES)
@@ -51,29 +49,4 @@ enum adf_device_type {
DEV_420XX,
DEV_6XXX,
};
-
-struct adf_dev_status_info {
- enum adf_device_type type;
- __u32 accel_id;
- __u32 instance_id;
- __u8 num_ae;
- __u8 num_accel;
- __u8 num_logical_accel;
- __u8 banks_per_accel;
- __u8 state;
- __u8 bus;
- __u8 dev;
- __u8 fun;
- char name[MAX_DEVICE_NAME_SIZE];
-};
-
-#define ADF_CTL_IOC_MAGIC 'a'
-#define IOCTL_CONFIG_SYS_RESOURCE_PARAMETERS _IOW(ADF_CTL_IOC_MAGIC, 0, \
- struct adf_user_cfg_ctl_data)
-#define IOCTL_STOP_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 1, \
- struct adf_user_cfg_ctl_data)
-#define IOCTL_START_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 2, \
- struct adf_user_cfg_ctl_data)
-#define IOCTL_STATUS_ACCEL_DEV _IOW(ADF_CTL_IOC_MAGIC, 3, __u32)
-#define IOCTL_GET_NUM_DEVICES _IOW(ADF_CTL_IOC_MAGIC, 4, __s32)
#endif
diff --git a/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h b/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h
deleted file mode 100644
index 421f4fb8b4dd..000000000000
--- a/drivers/crypto/intel/qat/qat_common/adf_cfg_user.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */
-/* Copyright(c) 2014 - 2020 Intel Corporation */
-#ifndef ADF_CFG_USER_H_
-#define ADF_CFG_USER_H_
-
-#include "adf_cfg_common.h"
-#include "adf_cfg_strings.h"
-
-struct adf_user_cfg_key_val {
- char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
- char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES];
- union {
- struct adf_user_cfg_key_val *next;
- __u64 padding3;
- };
- enum adf_cfg_val_type type;
-} __packed;
-
-struct adf_user_cfg_section {
- char name[ADF_CFG_MAX_SECTION_LEN_IN_BYTES];
- union {
- struct adf_user_cfg_key_val *params;
- __u64 padding1;
- };
- union {
- struct adf_user_cfg_section *next;
- __u64 padding3;
- };
-} __packed;
-
-struct adf_user_cfg_ctl_data {
- union {
- struct adf_user_cfg_section *config_section;
- __u64 padding;
- };
- __u8 device_id;
-} __packed;
-#endif
diff --git a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c
index c2e6f0cb7480..f01f2946de6e 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c
@@ -2,410 +2,13 @@
/* Copyright(c) 2014 - 2020 Intel Corporation */
#include <crypto/algapi.h>
+#include <linux/errno.h>
#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/bitops.h>
-#include <linux/pci.h>
-#include <linux/cdev.h>
-#include <linux/uaccess.h>
-#include "adf_accel_devices.h"
#include "adf_common_drv.h"
-#include "adf_cfg.h"
-#include "adf_cfg_common.h"
-#include "adf_cfg_user.h"
-
-#define ADF_CFG_MAX_SECTION 512
-#define ADF_CFG_MAX_KEY_VAL 256
-
-#define DEVICE_NAME "qat_adf_ctl"
-
-static DEFINE_MUTEX(adf_ctl_lock);
-static long adf_ctl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
-
-static const struct file_operations adf_ctl_ops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = adf_ctl_ioctl,
- .compat_ioctl = compat_ptr_ioctl,
-};
-
-static const struct class adf_ctl_class = {
- .name = DEVICE_NAME,
-};
-
-struct adf_ctl_drv_info {
- unsigned int major;
- struct cdev drv_cdev;
-};
-
-static struct adf_ctl_drv_info adf_ctl_drv;
-
-static void adf_chr_drv_destroy(void)
-{
- device_destroy(&adf_ctl_class, MKDEV(adf_ctl_drv.major, 0));
- cdev_del(&adf_ctl_drv.drv_cdev);
- class_unregister(&adf_ctl_class);
- unregister_chrdev_region(MKDEV(adf_ctl_drv.major, 0), 1);
-}
-
-static int adf_chr_drv_create(void)
-{
- dev_t dev_id;
- struct device *drv_device;
- int ret;
-
- if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME)) {
- pr_err("QAT: unable to allocate chrdev region\n");
- return -EFAULT;
- }
-
- ret = class_register(&adf_ctl_class);
- if (ret)
- goto err_chrdev_unreg;
-
- adf_ctl_drv.major = MAJOR(dev_id);
- cdev_init(&adf_ctl_drv.drv_cdev, &adf_ctl_ops);
- if (cdev_add(&adf_ctl_drv.drv_cdev, dev_id, 1)) {
- pr_err("QAT: cdev add failed\n");
- goto err_class_destr;
- }
-
- drv_device = device_create(&adf_ctl_class, NULL,
- MKDEV(adf_ctl_drv.major, 0),
- NULL, DEVICE_NAME);
- if (IS_ERR(drv_device)) {
- pr_err("QAT: failed to create device\n");
- goto err_cdev_del;
- }
- return 0;
-err_cdev_del:
- cdev_del(&adf_ctl_drv.drv_cdev);
-err_class_destr:
- class_unregister(&adf_ctl_class);
-err_chrdev_unreg:
- unregister_chrdev_region(dev_id, 1);
- return -EFAULT;
-}
-
-static struct adf_user_cfg_ctl_data *adf_ctl_alloc_resources(unsigned long arg)
-{
- struct adf_user_cfg_ctl_data *cfg_data;
-
- cfg_data = memdup_user((void __user *)arg, sizeof(*cfg_data));
- if (IS_ERR(cfg_data))
- pr_err("QAT: failed to copy from user cfg_data.\n");
- return cfg_data;
-}
-
-static int adf_add_key_value_data(struct adf_accel_dev *accel_dev,
- const char *section,
- const struct adf_user_cfg_key_val *key_val)
-{
- if (key_val->type == ADF_HEX) {
- long *ptr = (long *)key_val->val;
- long val = *ptr;
-
- if (adf_cfg_add_key_value_param(accel_dev, section,
- key_val->key, (void *)val,
- key_val->type)) {
- dev_err(&GET_DEV(accel_dev),
- "failed to add hex keyvalue.\n");
- return -EFAULT;
- }
- } else {
- if (adf_cfg_add_key_value_param(accel_dev, section,
- key_val->key, key_val->val,
- key_val->type)) {
- dev_err(&GET_DEV(accel_dev),
- "failed to add keyvalue.\n");
- return -EFAULT;
- }
- }
- return 0;
-}
-
-static int adf_copy_key_value_data(struct adf_accel_dev *accel_dev,
- struct adf_user_cfg_ctl_data *ctl_data)
-{
- struct adf_user_cfg_key_val key_val;
- struct adf_user_cfg_key_val *params_head;
- struct adf_user_cfg_section section, *section_head;
- int i, j;
-
- section_head = ctl_data->config_section;
-
- for (i = 0; section_head && i < ADF_CFG_MAX_SECTION; i++) {
- if (copy_from_user(§ion, (void __user *)section_head,
- sizeof(*section_head))) {
- dev_err(&GET_DEV(accel_dev),
- "failed to copy section info\n");
- goto out_err;
- }
-
- if (adf_cfg_section_add(accel_dev, section.name)) {
- dev_err(&GET_DEV(accel_dev),
- "failed to add section.\n");
- goto out_err;
- }
-
- params_head = section.params;
-
- for (j = 0; params_head && j < ADF_CFG_MAX_KEY_VAL; j++) {
- if (copy_from_user(&key_val, (void __user *)params_head,
- sizeof(key_val))) {
- dev_err(&GET_DEV(accel_dev),
- "Failed to copy keyvalue.\n");
- goto out_err;
- }
- if (adf_add_key_value_data(accel_dev, section.name,
- &key_val)) {
- goto out_err;
- }
- params_head = key_val.next;
- }
- section_head = section.next;
- }
- return 0;
-out_err:
- adf_cfg_del_all(accel_dev);
- return -EFAULT;
-}
-
-static int adf_ctl_ioctl_dev_config(struct file *fp, unsigned int cmd,
- unsigned long arg)
-{
- struct adf_user_cfg_ctl_data *ctl_data;
- struct adf_accel_dev *accel_dev;
- int ret = 0;
-
- ctl_data = adf_ctl_alloc_resources(arg);
- if (IS_ERR(ctl_data))
- return PTR_ERR(ctl_data);
-
- accel_dev = adf_devmgr_get_dev_by_id(ctl_data->device_id);
- if (!accel_dev) {
- ret = -EFAULT;
- goto out;
- }
-
- if (adf_dev_started(accel_dev)) {
- ret = -EFAULT;
- goto out;
- }
-
- if (adf_copy_key_value_data(accel_dev, ctl_data)) {
- ret = -EFAULT;
- goto out;
- }
- set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
-out:
- kfree(ctl_data);
- return ret;
-}
-
-static int adf_ctl_is_device_in_use(int id)
-{
- struct adf_accel_dev *dev;
-
- list_for_each_entry(dev, adf_devmgr_get_head(), list) {
- if (id == dev->accel_id || id == ADF_CFG_ALL_DEVICES) {
- if (adf_devmgr_in_reset(dev) || adf_dev_in_use(dev)) {
- dev_info(&GET_DEV(dev),
- "device qat_dev%d is busy\n",
- dev->accel_id);
- return -EBUSY;
- }
- }
- }
- return 0;
-}
-
-static void adf_ctl_stop_devices(u32 id)
-{
- struct adf_accel_dev *accel_dev;
-
- list_for_each_entry(accel_dev, adf_devmgr_get_head(), list) {
- if (id == accel_dev->accel_id || id == ADF_CFG_ALL_DEVICES) {
- if (!adf_dev_started(accel_dev))
- continue;
-
- /* First stop all VFs */
- if (!accel_dev->is_vf)
- continue;
-
- adf_dev_down(accel_dev);
- }
- }
-
- list_for_each_entry(accel_dev, adf_devmgr_get_head(), list) {
- if (id == accel_dev->accel_id || id == ADF_CFG_ALL_DEVICES) {
- if (!adf_dev_started(accel_dev))
- continue;
-
- adf_dev_down(accel_dev);
- }
- }
-}
-
-static int adf_ctl_ioctl_dev_stop(struct file *fp, unsigned int cmd,
- unsigned long arg)
-{
- int ret;
- struct adf_user_cfg_ctl_data *ctl_data;
-
- ctl_data = adf_ctl_alloc_resources(arg);
- if (IS_ERR(ctl_data))
- return PTR_ERR(ctl_data);
-
- if (adf_devmgr_verify_id(ctl_data->device_id)) {
- pr_err("QAT: Device %d not found\n", ctl_data->device_id);
- ret = -ENODEV;
- goto out;
- }
-
- ret = adf_ctl_is_device_in_use(ctl_data->device_id);
- if (ret)
- goto out;
-
- if (ctl_data->device_id == ADF_CFG_ALL_DEVICES)
- pr_info("QAT: Stopping all acceleration devices.\n");
- else
- pr_info("QAT: Stopping acceleration device qat_dev%d.\n",
- ctl_data->device_id);
-
- adf_ctl_stop_devices(ctl_data->device_id);
-
-out:
- kfree(ctl_data);
- return ret;
-}
-
-static int adf_ctl_ioctl_dev_start(struct file *fp, unsigned int cmd,
- unsigned long arg)
-{
- int ret;
- struct adf_user_cfg_ctl_data *ctl_data;
- struct adf_accel_dev *accel_dev;
-
- ctl_data = adf_ctl_alloc_resources(arg);
- if (IS_ERR(ctl_data))
- return PTR_ERR(ctl_data);
-
- ret = -ENODEV;
- accel_dev = adf_devmgr_get_dev_by_id(ctl_data->device_id);
- if (!accel_dev)
- goto out;
-
- dev_info(&GET_DEV(accel_dev),
- "Starting acceleration device qat_dev%d.\n",
- ctl_data->device_id);
-
- ret = adf_dev_up(accel_dev, false);
-
- if (ret) {
- dev_err(&GET_DEV(accel_dev), "Failed to start qat_dev%d\n",
- ctl_data->device_id);
- adf_dev_down(accel_dev);
- }
-out:
- kfree(ctl_data);
- return ret;
-}
-
-static int adf_ctl_ioctl_get_num_devices(struct file *fp, unsigned int cmd,
- unsigned long arg)
-{
- u32 num_devices = 0;
-
- adf_devmgr_get_num_dev(&num_devices);
- if (copy_to_user((void __user *)arg, &num_devices, sizeof(num_devices)))
- return -EFAULT;
-
- return 0;
-}
-
-static int adf_ctl_ioctl_get_status(struct file *fp, unsigned int cmd,
- unsigned long arg)
-{
- struct adf_hw_device_data *hw_data;
- struct adf_dev_status_info dev_info;
- struct adf_accel_dev *accel_dev;
-
- if (copy_from_user(&dev_info, (void __user *)arg,
- sizeof(struct adf_dev_status_info))) {
- pr_err("QAT: failed to copy from user.\n");
- return -EFAULT;
- }
-
- accel_dev = adf_devmgr_get_dev_by_id(dev_info.accel_id);
- if (!accel_dev)
- return -ENODEV;
-
- hw_data = accel_dev->hw_device;
- dev_info.state = adf_dev_started(accel_dev) ? DEV_UP : DEV_DOWN;
- dev_info.num_ae = hw_data->get_num_aes(hw_data);
- dev_info.num_accel = hw_data->get_num_accels(hw_data);
- dev_info.num_logical_accel = hw_data->num_logical_accel;
- dev_info.banks_per_accel = hw_data->num_banks
- / hw_data->num_logical_accel;
- strscpy(dev_info.name, hw_data->dev_class->name, sizeof(dev_info.name));
- dev_info.instance_id = hw_data->instance_id;
- dev_info.type = hw_data->dev_class->type;
- dev_info.bus = accel_to_pci_dev(accel_dev)->bus->number;
- dev_info.dev = PCI_SLOT(accel_to_pci_dev(accel_dev)->devfn);
- dev_info.fun = PCI_FUNC(accel_to_pci_dev(accel_dev)->devfn);
-
- if (copy_to_user((void __user *)arg, &dev_info,
- sizeof(struct adf_dev_status_info))) {
- dev_err(&GET_DEV(accel_dev), "failed to copy status.\n");
- return -EFAULT;
- }
- return 0;
-}
-
-static long adf_ctl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
-{
- int ret;
-
- if (mutex_lock_interruptible(&adf_ctl_lock))
- return -EFAULT;
-
- switch (cmd) {
- case IOCTL_CONFIG_SYS_RESOURCE_PARAMETERS:
- ret = adf_ctl_ioctl_dev_config(fp, cmd, arg);
- break;
-
- case IOCTL_STOP_ACCEL_DEV:
- ret = adf_ctl_ioctl_dev_stop(fp, cmd, arg);
- break;
-
- case IOCTL_START_ACCEL_DEV:
- ret = adf_ctl_ioctl_dev_start(fp, cmd, arg);
- break;
-
- case IOCTL_GET_NUM_DEVICES:
- ret = adf_ctl_ioctl_get_num_devices(fp, cmd, arg);
- break;
-
- case IOCTL_STATUS_ACCEL_DEV:
- ret = adf_ctl_ioctl_get_status(fp, cmd, arg);
- break;
- default:
- pr_err_ratelimited("QAT: Invalid ioctl %d\n", cmd);
- ret = -EFAULT;
- break;
- }
- mutex_unlock(&adf_ctl_lock);
- return ret;
-}
static int __init adf_register_ctl_device_driver(void)
{
- if (adf_chr_drv_create())
- goto err_chr_dev;
-
if (adf_init_misc_wq())
goto err_misc_wq;
@@ -437,15 +40,11 @@ static int __init adf_register_ctl_device_driver(void)
err_aer:
adf_exit_misc_wq();
err_misc_wq:
- adf_chr_drv_destroy();
-err_chr_dev:
- mutex_destroy(&adf_ctl_lock);
return -EFAULT;
}
static void __exit adf_unregister_ctl_device_driver(void)
{
- adf_chr_drv_destroy();
adf_exit_misc_wq();
adf_exit_aer();
adf_exit_vf_wq();
@@ -453,7 +52,6 @@ static void __exit adf_unregister_ctl_device_driver(void)
qat_crypto_unregister();
qat_compression_unregister();
adf_clean_vf_map(false);
- mutex_destroy(&adf_ctl_lock);
}
module_init(adf_register_ctl_device_driver);
--
2.54.0
^ permalink raw reply related
* [PATCH 2/2] crypto: qat - rename adf_ctl_drv.c to adf_module.c
From: Giovanni Cabiddu @ 2026-05-08 9:18 UTC (permalink / raw)
To: herbert
Cc: linux-crypto, qat-linux, wangzhi, byu, w15303746062, vdronov,
Giovanni Cabiddu, Ahsan Atta
In-Reply-To: <20260508091912.206913-1-giovanni.cabiddu@intel.com>
Now that the character device and IOCTL interface have been removed,
adf_ctl_drv.c only contains module_init/module_exit hooks. Rename it
to adf_module.c to better reflect its purpose and rename the init/exit
functions accordingly.
Signed-off-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
Reviewed-by: Ahsan Atta <ahsan.atta@intel.com>
---
drivers/crypto/intel/qat/qat_common/Makefile | 2 +-
.../intel/qat/qat_common/{adf_ctl_drv.c => adf_module.c} | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
rename drivers/crypto/intel/qat/qat_common/{adf_ctl_drv.c => adf_module.c} (84%)
diff --git a/drivers/crypto/intel/qat/qat_common/Makefile b/drivers/crypto/intel/qat/qat_common/Makefile
index 9478111c8437..2b1649001518 100644
--- a/drivers/crypto/intel/qat/qat_common/Makefile
+++ b/drivers/crypto/intel/qat/qat_common/Makefile
@@ -9,7 +9,6 @@ intel_qat-y := adf_accel_engine.o \
adf_cfg.o \
adf_cfg_services.o \
adf_clock.o \
- adf_ctl_drv.o \
adf_dc.o \
adf_dev_mgr.o \
adf_gen2_config.o \
@@ -26,6 +25,7 @@ intel_qat-y := adf_accel_engine.o \
adf_hw_arbiter.o \
adf_init.o \
adf_isr.o \
+ adf_module.o \
adf_mstate_mgr.o \
adf_rl_admin.o \
adf_rl.o \
diff --git a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/intel/qat/qat_common/adf_module.c
similarity index 84%
rename from drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c
rename to drivers/crypto/intel/qat/qat_common/adf_module.c
index f01f2946de6e..fccaa71eeedc 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_module.c
@@ -7,7 +7,7 @@
#include "adf_common_drv.h"
-static int __init adf_register_ctl_device_driver(void)
+static int __init adf_register_module(void)
{
if (adf_init_misc_wq())
goto err_misc_wq;
@@ -43,7 +43,7 @@ static int __init adf_register_ctl_device_driver(void)
return -EFAULT;
}
-static void __exit adf_unregister_ctl_device_driver(void)
+static void __exit adf_unregister_module(void)
{
adf_exit_misc_wq();
adf_exit_aer();
@@ -54,8 +54,8 @@ static void __exit adf_unregister_ctl_device_driver(void)
adf_clean_vf_map(false);
}
-module_init(adf_register_ctl_device_driver);
-module_exit(adf_unregister_ctl_device_driver);
+module_init(adf_register_module);
+module_exit(adf_unregister_module);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Intel");
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] crypto: qat - fix use-after-free during concurrent device start and removal
From: Giovanni Cabiddu @ 2026-05-08 9:24 UTC (permalink / raw)
To: w15303746062@163.com
Cc: herbert@gondor.apana.org.au, davem@davemloft.net, qat-linux,
linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org,
Mingyu Wang
In-Reply-To: <20260504025120.98242-1-w15303746062@163.com>
Hi Mingyu,
Thanks for your patches.
The ioctl interface exposed by the QAT driver is not part of any public
uAPI header and has no known users. I just sent a series that removes it
entirely [1], which also eliminates this issue.
[1] https://lore.kernel.org/all/20260508091912.206913-1-giovanni.cabiddu@intel.com/
Regards,
--
Giovanni
On Mon, May 04, 2026 at 03:51:20AM +0100, w15303746062@163.com wrote:
> From: Mingyu Wang <25181214217@stu.xidian.edu.cn>
>
> A Use-After-Free (UAF) vulnerability was identified in the QAT driver's ioctl path. When handling commands like IOCTL_START_ACCEL_DEV, `adf_ctl_ioctl_dev_start()` retrieves the acceleration device using `adf_devmgr_get_dev_by_id()`.
>
> Previously, this lookup function iterated over the `accel_table` under the `table_lock`. However, once the target device was found, the lock was dropped and a bare pointer was returned without incrementing the device's reference count.
>
> This creates a critical race condition. If a concurrent thread removes the device (e.g., via device stop operations or PCIe hotplug) by calling `adf_devmgr_rm_dev()`, the device is removed from the list and its memory is subsequently freed. When the original ioctl thread resumes and attempts to acquire `accel_dev->state_lock` inside `adf_dev_up()`, it triggers a KASAN slab-out-of-bounds panic.
>
> Fix this by properly leveraging the existing `ref_count`. Increment the device's `ref_count` via `atomic_inc()` inside `adf_devmgr_get_dev_by_id()` while the `table_lock` is still held. All callers of `adf_devmgr_get_dev_by_id()` are then updated to safely release this reference using `atomic_dec(&accel_dev->ref_count)` once they are done interacting with the device.
>
> Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
> ---
> drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c | 10 ++++++++++
> drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c | 12 ++++++++++--
> 2 files changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c
> index c2e6f0cb7480..4924b2bbb412 100644
> --- a/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c
> +++ b/drivers/crypto/intel/qat/qat_common/adf_ctl_drv.c
> @@ -201,6 +201,9 @@ static int adf_ctl_ioctl_dev_config(struct file *fp, unsigned int cmd,
> }
> set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
> out:
> + /* Release the reference acquired by adf_devmgr_get_dev_by_id() */
> + if (accel_dev)
> + atomic_dec(&accel_dev->ref_count);
> kfree(ctl_data);
> return ret;
> }
> @@ -310,6 +313,9 @@ static int adf_ctl_ioctl_dev_start(struct file *fp, unsigned int cmd,
> adf_dev_down(accel_dev);
> }
> out:
> + /* Release the reference acquired by adf_devmgr_get_dev_by_id() */
> + if (accel_dev)
> + atomic_dec(&accel_dev->ref_count);
> kfree(ctl_data);
> return ret;
> }
> @@ -360,8 +366,12 @@ static int adf_ctl_ioctl_get_status(struct file *fp, unsigned int cmd,
> if (copy_to_user((void __user *)arg, &dev_info,
> sizeof(struct adf_dev_status_info))) {
> dev_err(&GET_DEV(accel_dev), "failed to copy status.\n");
> + atomic_dec(&accel_dev->ref_count);
> return -EFAULT;
> }
> +
> + /* Release the reference acquired by adf_devmgr_get_dev_by_id() */
> + atomic_dec(&accel_dev->ref_count);
> return 0;
> }
>
> diff --git a/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c b/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c
> index e050de16ab5d..321bea3cefce 100644
> --- a/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c
> +++ b/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c
> @@ -320,6 +320,8 @@ struct adf_accel_dev *adf_devmgr_get_dev_by_id(u32 id)
> struct adf_accel_dev *ptr =
> list_entry(itr, struct adf_accel_dev, list);
> if (ptr->accel_id == id) {
> + /* Increment ref_count to prevent UAF during concurrent removal */
> + atomic_inc(&ptr->ref_count);
> mutex_unlock(&table_lock);
> return ptr;
> }
> @@ -331,11 +333,17 @@ struct adf_accel_dev *adf_devmgr_get_dev_by_id(u32 id)
>
> int adf_devmgr_verify_id(u32 id)
> {
> + struct adf_accel_dev *accel_dev;
> +
> if (id == ADF_CFG_ALL_DEVICES)
> return 0;
>
> - if (adf_devmgr_get_dev_by_id(id))
> - return 0;
> + accel_dev = adf_devmgr_get_dev_by_id(id);
> + if (accel_dev) {
> + /* Release the reference immediately as we only verify existence */
> + atomic_dec(&accel_dev->ref_count);
> + return 0;
> + }
>
> return -ENODEV;
> }
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH v2] dt-bindings: crypto: qcom,inline-crypto-engine: Document Nord ICE
From: Shawn Guo @ 2026-05-08 10:15 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Herbert Xu, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Dmitry Baryshkov,
Bartosz Golaszewski, Deepti Jaggi, linux-crypto, devicetree,
linux-arm-msm, linux-kernel
In-Reply-To: <20260429-important-panther-of-drama-00f5af@quoll>
On Wed, Apr 29, 2026 at 09:15:35AM +0200, Krzysztof Kozlowski wrote:
> On Mon, Apr 27, 2026 at 09:05:27AM +0800, Shawn Guo wrote:
> > Document Inline Crypto Engine (ICE) on Qualcomm Nord SoC which is
> > compatible with 'qcom,inline-crypto-engine'.
> >
> > Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> > ---
> > Changes in v2:
> > - Improve commit log to make the compatibility explicit
> > - Link to v1: https://lore.kernel.org/all/20260420073301.1250197-1-shengchao.guo@oss.qualcomm.com/
> >
>
> Same comment as for SoC patches - do not split patches targetting same
> maintainer - crypto - into separate patchsets. It's one patchset.
I will have to resend this one alone due to the request from Harshal,
rebasing on his ICE binding update series[1].
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Thank you!
Shawn
[1] https://lore.kernel.org/all/b8805117-d54f-4e42-a7d4-6fa18af63e69@oss.qualcomm.com/
^ permalink raw reply
* [PATCH] crypto: use designated initializers for report structs
From: Thorsten Blum @ 2026-05-08 10:57 UTC (permalink / raw)
To: Herbert Xu, David S. Miller; +Cc: Thorsten Blum, linux-crypto, linux-kernel
Use designated initializers for the report structs instead of clearing
the struct with memset() and then copying fixed strings with strscpy()
at runtime.
This keeps the structs zero-initialized, lets the compiler diagnose
oversized string literals, and makes the code easier to read.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
crypto/acompress.c | 8 +++-----
crypto/aead.c | 10 ++++------
crypto/ahash.c | 8 +++-----
crypto/akcipher.c | 8 +++-----
crypto/crypto_user.c | 14 ++++++--------
crypto/kpp.c | 8 +++-----
crypto/lskcipher.c | 10 ++++------
crypto/rng.c | 8 +++-----
crypto/scompress.c | 8 +++-----
crypto/shash.c | 8 +++-----
crypto/sig.c | 6 +++---
crypto/skcipher.c | 10 ++++------
12 files changed, 42 insertions(+), 64 deletions(-)
diff --git a/crypto/acompress.c b/crypto/acompress.c
index 6025c1acce49..032de704eb2c 100644
--- a/crypto/acompress.c
+++ b/crypto/acompress.c
@@ -51,11 +51,9 @@ static inline struct acomp_alg *crypto_acomp_alg(struct crypto_acomp *tfm)
static int __maybe_unused crypto_acomp_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_acomp racomp;
-
- memset(&racomp, 0, sizeof(racomp));
-
- strscpy(racomp.type, "acomp", sizeof(racomp.type));
+ struct crypto_report_acomp racomp = {
+ .type = "acomp",
+ };
return nla_put(skb, CRYPTOCFGA_REPORT_ACOMP, sizeof(racomp), &racomp);
}
diff --git a/crypto/aead.c b/crypto/aead.c
index e009937bf3a5..045b74c3779f 100644
--- a/crypto/aead.c
+++ b/crypto/aead.c
@@ -136,13 +136,11 @@ static int crypto_aead_init_tfm(struct crypto_tfm *tfm)
static int __maybe_unused crypto_aead_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_aead raead;
struct aead_alg *aead = container_of(alg, struct aead_alg, base);
-
- memset(&raead, 0, sizeof(raead));
-
- strscpy(raead.type, "aead", sizeof(raead.type));
- strscpy(raead.geniv, "<none>", sizeof(raead.geniv));
+ struct crypto_report_aead raead = {
+ .type = "aead",
+ .geniv = "<none>",
+ };
raead.blocksize = alg->cra_blocksize;
raead.maxauthsize = aead->maxauthsize;
diff --git a/crypto/ahash.c b/crypto/ahash.c
index 7a730324c50e..dd56b0e45c0d 100644
--- a/crypto/ahash.c
+++ b/crypto/ahash.c
@@ -789,11 +789,9 @@ static void crypto_ahash_free_instance(struct crypto_instance *inst)
static int __maybe_unused crypto_ahash_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_hash rhash;
-
- memset(&rhash, 0, sizeof(rhash));
-
- strscpy(rhash.type, "ahash", sizeof(rhash.type));
+ struct crypto_report_hash rhash = {
+ .type = "ahash",
+ };
rhash.blocksize = alg->cra_blocksize;
rhash.digestsize = __crypto_hash_alg_common(alg)->digestsize;
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
index dfe87b3ce183..630bb19738be 100644
--- a/crypto/akcipher.c
+++ b/crypto/akcipher.c
@@ -36,11 +36,9 @@ struct crypto_akcipher_sync_data {
static int __maybe_unused crypto_akcipher_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_akcipher rakcipher;
-
- memset(&rakcipher, 0, sizeof(rakcipher));
-
- strscpy(rakcipher.type, "akcipher", sizeof(rakcipher.type));
+ struct crypto_report_akcipher rakcipher = {
+ .type = "akcipher",
+ };
return nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER,
sizeof(rakcipher), &rakcipher);
diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c
index 3187e0d276f9..e8b6ae75f31f 100644
--- a/crypto/crypto_user.c
+++ b/crypto/crypto_user.c
@@ -70,11 +70,9 @@ static struct crypto_alg *crypto_alg_match(struct crypto_user_alg *p, int exact)
static int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_cipher rcipher;
-
- memset(&rcipher, 0, sizeof(rcipher));
-
- strscpy(rcipher.type, "cipher", sizeof(rcipher.type));
+ struct crypto_report_cipher rcipher = {
+ .type = "cipher",
+ };
rcipher.blocksize = alg->cra_blocksize;
rcipher.min_keysize = alg->cra_cipher.cia_min_keysize;
@@ -103,10 +101,10 @@ static int crypto_report_one(struct crypto_alg *alg,
if (nla_put_u32(skb, CRYPTOCFGA_PRIORITY_VAL, alg->cra_priority))
goto nla_put_failure;
if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
- struct crypto_report_larval rl;
+ struct crypto_report_larval rl = {
+ .type = "larval",
+ };
- memset(&rl, 0, sizeof(rl));
- strscpy(rl.type, "larval", sizeof(rl.type));
if (nla_put(skb, CRYPTOCFGA_REPORT_LARVAL, sizeof(rl), &rl))
goto nla_put_failure;
goto out;
diff --git a/crypto/kpp.c b/crypto/kpp.c
index 7451d39a7ad8..522c352a03af 100644
--- a/crypto/kpp.c
+++ b/crypto/kpp.c
@@ -20,11 +20,9 @@
static int __maybe_unused crypto_kpp_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_kpp rkpp;
-
- memset(&rkpp, 0, sizeof(rkpp));
-
- strscpy(rkpp.type, "kpp", sizeof(rkpp.type));
+ struct crypto_report_kpp rkpp = {
+ .type = "kpp",
+ };
return nla_put(skb, CRYPTOCFGA_REPORT_KPP, sizeof(rkpp), &rkpp);
}
diff --git a/crypto/lskcipher.c b/crypto/lskcipher.c
index bb166250b732..e4328df6e26c 100644
--- a/crypto/lskcipher.c
+++ b/crypto/lskcipher.c
@@ -264,12 +264,10 @@ static int __maybe_unused crypto_lskcipher_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
struct lskcipher_alg *skcipher = __crypto_lskcipher_alg(alg);
- struct crypto_report_blkcipher rblkcipher;
-
- memset(&rblkcipher, 0, sizeof(rblkcipher));
-
- strscpy(rblkcipher.type, "lskcipher", sizeof(rblkcipher.type));
- strscpy(rblkcipher.geniv, "<none>", sizeof(rblkcipher.geniv));
+ struct crypto_report_blkcipher rblkcipher = {
+ .type = "lskcipher",
+ .geniv = "<none>",
+ };
rblkcipher.blocksize = alg->cra_blocksize;
rblkcipher.min_keysize = skcipher->co.min_keysize;
diff --git a/crypto/rng.c b/crypto/rng.c
index 1d4b9177bad4..eec786c45bdd 100644
--- a/crypto/rng.c
+++ b/crypto/rng.c
@@ -65,11 +65,9 @@ static unsigned int seedsize(struct crypto_alg *alg)
static int __maybe_unused crypto_rng_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_rng rrng;
-
- memset(&rrng, 0, sizeof(rrng));
-
- strscpy(rrng.type, "rng", sizeof(rrng.type));
+ struct crypto_report_rng rrng = {
+ .type = "rng",
+ };
rrng.seedsize = seedsize(alg);
diff --git a/crypto/scompress.c b/crypto/scompress.c
index 253655ece83f..de54227203ce 100644
--- a/crypto/scompress.c
+++ b/crypto/scompress.c
@@ -48,11 +48,9 @@ static DECLARE_WORK(scomp_scratch_work, scomp_scratch_workfn);
static int __maybe_unused crypto_scomp_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_comp rscomp;
-
- memset(&rscomp, 0, sizeof(rscomp));
-
- strscpy(rscomp.type, "scomp", sizeof(rscomp.type));
+ struct crypto_report_comp rscomp = {
+ .type = "scomp",
+ };
return nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS,
sizeof(rscomp), &rscomp);
diff --git a/crypto/shash.c b/crypto/shash.c
index 2f07d0bd1f61..d31a65570f69 100644
--- a/crypto/shash.c
+++ b/crypto/shash.c
@@ -333,12 +333,10 @@ static void crypto_shash_free_instance(struct crypto_instance *inst)
static int __maybe_unused crypto_shash_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
- struct crypto_report_hash rhash;
struct shash_alg *salg = __crypto_shash_alg(alg);
-
- memset(&rhash, 0, sizeof(rhash));
-
- strscpy(rhash.type, "shash", sizeof(rhash.type));
+ struct crypto_report_hash rhash = {
+ .type = "shash",
+ };
rhash.blocksize = alg->cra_blocksize;
rhash.digestsize = salg->digestsize;
diff --git a/crypto/sig.c b/crypto/sig.c
index beba745b6405..7d2048da5c3a 100644
--- a/crypto/sig.c
+++ b/crypto/sig.c
@@ -53,9 +53,9 @@ static void __maybe_unused crypto_sig_show(struct seq_file *m,
static int __maybe_unused crypto_sig_report(struct sk_buff *skb,
struct crypto_alg *alg)
{
- struct crypto_report_sig rsig = {};
-
- strscpy(rsig.type, "sig", sizeof(rsig.type));
+ struct crypto_report_sig rsig = {
+ .type = "sig",
+ };
return nla_put(skb, CRYPTOCFGA_REPORT_SIG, sizeof(rsig), &rsig);
}
diff --git a/crypto/skcipher.c b/crypto/skcipher.c
index 2b31d1d5d268..617e840432b1 100644
--- a/crypto/skcipher.c
+++ b/crypto/skcipher.c
@@ -591,12 +591,10 @@ static int __maybe_unused crypto_skcipher_report(
struct sk_buff *skb, struct crypto_alg *alg)
{
struct skcipher_alg *skcipher = __crypto_skcipher_alg(alg);
- struct crypto_report_blkcipher rblkcipher;
-
- memset(&rblkcipher, 0, sizeof(rblkcipher));
-
- strscpy(rblkcipher.type, "skcipher", sizeof(rblkcipher.type));
- strscpy(rblkcipher.geniv, "<none>", sizeof(rblkcipher.geniv));
+ struct crypto_report_blkcipher rblkcipher = {
+ .type = "skcipher",
+ .geniv = "<none>",
+ };
rblkcipher.blocksize = alg->cra_blocksize;
rblkcipher.min_keysize = skcipher->min_keysize;
^ permalink raw reply related
* [PATCH] crypto : ecc - Fix carry overflow in vli multiplication
From: Anastasia Tishchenko @ 2026-05-08 11:48 UTC (permalink / raw)
To: Lukas Wunner, Ignat Korchagin, Stefan Berger, Herbert Xu,
David S . Miller
Cc: linux-crypto, linux-kernel, Anastasia Tishchenko
The carry flag calculation fails when r01.m_high is saturated
(0xFFFFFFFFFFFFFFFF) and addition of lower bits overflows.
The condition (r01.m_high < product.m_high) doesn't handle the case
where r01.m_high == product.m_high and an additional carry exists
from lower-bit overflow.
Add proper handling for this boundary by accounting for the carry
from the lower addition.
This issue was discovered during formal verification of ECC functions.
Signed-off-by: Anastasia Tishchenko <sv3iry@gmail.com>
---
crypto/ecc.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/crypto/ecc.c b/crypto/ecc.c
index 43b0def3a225..dfe96471407c 100644
--- a/crypto/ecc.c
+++ b/crypto/ecc.c
@@ -427,7 +427,10 @@ static void vli_mult(u64 *result, const u64 *left, const u64 *right,
product = mul_64_64(left[i], right[k - i]);
r01 = add_128_128(r01, product);
- r2 += (r01.m_high < product.m_high);
+ if (r01.m_high != product.m_high)
+ r2 += (r01.m_high < product.m_high);
+ else
+ r2 += (r01.m_low < product.m_low);
}
result[k] = r01.m_low;
@@ -488,7 +491,10 @@ static void vli_square(u64 *result, const u64 *left, unsigned int ndigits)
}
r01 = add_128_128(r01, product);
- r2 += (r01.m_high < product.m_high);
+ if (r01.m_high != product.m_high)
+ r2 += (r01.m_high < product.m_high);
+ else
+ r2 += (r01.m_low < product.m_low);
}
result[k] = r01.m_low;
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 00/17] ext4/lib-crc: LBS performance part 1 - incremental CRC32c for bitmap checksums
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
Motivation
==========
In [1] we added large block size (LBS) support to ext4. After enabling
LBS we observed several performance bottlenecks:
1. Checksum computation (bitmap, extent block, dir block) becomes
significantly more expensive as the block size grows.
2. Free-bit searches (_find_next_bit) over large bitmaps become costly.
CRC32c is linear over GF(2), so when a contiguous range of bits in a
buffer is flipped the new checksum can be derived from the old one
without re-scanning the entire buffer:
New_CRC = Old_CRC ^ CRC(flip_mask << trailing_bits)
This series introduces crc32c_flip_range() in lib/crc and applies it to
ext4's inode and block bitmap checksum paths, reducing checksum overhead
from O(N) to O(log N) per update.
For dir blocks and extent blocks, each modification touches a 12-264
byte region; by computing the CRC of the modified region before and
after the change we can derive the delta to the overall checksum. A
crc32c_splice() API implementing this approach has been developed
locally and will be posted shortly.
For the _find_next_bit bottleneck under LBS, a per-block-group free
space rb-tree can accelerate lookups. A local prototype exists and is
still being tested; feedback and alternative approaches are welcome.
[1]: https://lore.kernel.org/all/20251121090654.631996-1-libaokun@huaweicloud.com
Benchmark (full roadmap projection)
====================================
Single-process sequential fallocate of 64K blocks. All throughput
values are in GB/s; percentages in parentheses show improvement over
the unpatched baseline. "+crc_splice" and "+free_space_tree" columns
show expected gains from follow-up series (not included here).
* Blocks per group up to 65528 (default e2fsprogs limit)
+--------+---------+-----------------+-----------------+---------------------+
| Blksz | Before | +crc_flip_range | +crc_splice | +free_space_tree |
+--------+---------+-----------------+-----------------+---------------------+
| 1k | 14.9 | 15.0 (+0.7%) | 15.2 (+2.0%) | 15.3 (+2.7%) |
| 2k | 17.5 | 17.8 (+1.7%) | 18.2 (+4.0%) | 18.7 (+6.9%) |
| 4k | 16.8 | 17.4 (+3.6%) | 18.3 (+8.9%) | 18.4 (+9.5%) |
| 8k | 15.5 | 16.5 (+6.5%) | 18.3 (+18.1%) | 18.2 (+17.4%) |
| 16k | 12.6 | 13.2 (+4.8%) | 15.9 (+26.2%) | 15.9 (+26.2%) |
| 32k | 8.99 | 9.60 (+6.8%) | 12.3 (+36.8%) | 12.5 (+39.0%) |
| 64k | 8.24 | 8.54 (+3.6%) | 14.0 (+69.9%) | 19.4 (+135%) |
+--------+---------+-----------------+-----------------+---------------------+
* Blocks per group up to 524288 (e2fsprogs limit lifted)
+--------+---------+-----------------+-----------------+---------------------+
| Blksz | Before | +crc_flip_range | +crc_splice | +free_space_tree |
+--------+---------+-----------------+-----------------+---------------------+
| 1k | 15.0 | 14.9 (-0.7%) | 15.5 (+3.3%) | 15.6 (+4.0%) |
| 2k | 17.4 | 17.7 (+1.7%) | 17.9 (+2.9%) | 18.2 (+4.6%) |
| 4k | 16.7 | 17.3 (+3.6%) | 18.4 (+10.2%) | 18.7 (+12.0%) |
| 8k | 15.7 | 16.4 (+4.5%) | 19.1 (+21.7%) | 19.3 (+22.9%) |
| 16k | 13.5 | 15.4 (+14.1%) | 18.7 (+38.5%) | 19.0 (+40.7%) |
| 32k | 9.64 | 12.3 (+27.6%) | 17.7 (+83.5%) | 17.7 (+83.5%) |
| 64k | 2.84 | 3.17 (+11.6%) | 3.48 (+22.5%) | 19.8 (+597%) |
+--------+---------+-----------------+-----------------+---------------------+
Patch Overview
==============
* Patches 1-3 (lib/crc): Introduce crc32c_flip_range() with O(log N)
complexity using precomputed GF(2) shift matrices and nibble-indexed
lookup tables (~9.8KB, fits in L1 cache). Add kunit tests and
benchmarks.
* Patch 4: Fix incorrect free clusters accounting when allocated blocks
overlap with filesystem metadata on a corrupted filesystem.
* Patches 5-7: Extract block bitmap checksum helpers, add the
incremental update wrapper ext4_block_bitmap_csum_set_range(), and
use it in ext4_mb_mark_context().
* Patches 8-10: Extract inode bitmap checksum helpers, add
ext4_inode_bitmap_csum_set_fast(), and use it in ext4_free_inode().
* Patch 11: Fix missing bg_used_dirs_count update during fast commit
replay.
* Patches 12-13: Factor out ext4_might_init_block_bitmap() and merge
bitmap modification with GDP update under a single group lock in
ext4_mark_inode_used(), eliminating a race window.
* Patches 14-15: Rename 'ino' to 'bit' in __ext4_new_inode() for
clarity, then merge bitmap modification and GDP update under a
single group lock with incremental CRC.
* Patches 16-17: Extract ext4_update_inode_group_desc() and
ext4_get_flex_group() helpers to reduce code duplication.
Testing
=======
"kvm-xfstests -c ext4/all -g auto" has been executed with no new failures.
crc32c_flip_range() micro-benchmark on Intel Xeon (Ice Lake) with
CRC32c hardware acceleration:
bitmap: 1024 2048 4096 8192 16384 32768 65536
flip(ns): 48 53 57 63 68 73 78
full(ns): 45 88 182 357 709 1421 2853
speedup: 0.9x 1.6x 3.1x 5.6x 10.3x 19.3x 36.3x
Comments and questions are, as always, welcome.
Thanks,
Baokun
Baokun Li (17):
lib/crc: add crc32c_flip_range() for incremental CRC update
lib/crc: crc_kunit: add kunit test for crc32c_flip_range()
lib/crc: crc_kunit: add benchmark for crc32c_flip_range()
ext4: fix incorrect block bitmap free clusters update on metadata
overlap
ext4: extract block bitmap checksum get and store helpers
ext4: add ext4_block_bitmap_csum_set_range() for incremental checksum
update
ext4: use fast incremental CRC update in ext4_mb_mark_context()
ext4: extract inode bitmap checksum get and store helpers
ext4: add ext4_inode_bitmap_csum_set_fast() for incremental checksum
update
ext4: use fast incremental CRC update in ext4_free_inode()
ext4: fix missing bg_used_dirs_count update in fast commit replay
ext4: factor out ext4_might_init_block_bitmap() helper
ext4: use fast incremental CRC update in ext4_mark_inode_used()
ext4: rename ino to bit in __ext4_new_inode()
ext4: use fast incremental CRC update in __ext4_new_inode()
ext4: extract ext4_update_inode_group_desc() to reduce duplication
ext4: add ext4_get_flex_group() helper to simplify flex group lookups
fs/ext4/bitmap.c | 109 ++++++++--
fs/ext4/ext4.h | 15 +-
fs/ext4/fast_commit.c | 13 +-
fs/ext4/ialloc.c | 343 ++++++++++++++------------------
fs/ext4/mballoc.c | 28 ++-
fs/ext4/resize.c | 4 +-
fs/ext4/super.c | 4 +-
include/linux/crc32.h | 25 +++
lib/crc/.gitignore | 2 +
lib/crc/Makefile | 13 +-
lib/crc/crc32c-incr.c | 140 +++++++++++++
lib/crc/gen_crc32c_incr_table.c | 141 +++++++++++++
lib/crc/tests/crc_kunit.c | 137 +++++++++++++
13 files changed, 746 insertions(+), 228 deletions(-)
create mode 100644 lib/crc/crc32c-incr.c
create mode 100644 lib/crc/gen_crc32c_incr_table.c
--
2.43.7
^ permalink raw reply
* [PATCH RFC 04/17] ext4: fix incorrect block bitmap free clusters update on metadata overlap
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
In ext4_mb_mark_diskspace_used(), when the allocator detects that the
allocated blocks overlap with filesystem metadata, it enters an error
recovery path that marks these blocks as used in the bitmap via
ext4_mb_mark_context() with flags=0.
Without EXT4_MB_BITMAP_MARKED_CHECK, ext4_mb_mark_context() assumes all
bits in the range will be flipped, so it sets changed=len unconditionally.
However, in a corrupted filesystem, some of these metadata blocks may
already be marked as used (bit=1) in the bitmap. Since mb_set_bits() is
idempotent (sets bits to 1 regardless of current state), bits that are
already set won't actually change, but the free clusters count is still
decremented by the full range length, leading to an inaccurate free
clusters count.
Fix this by passing EXT4_MB_BITMAP_MARKED_CHECK, which correctly counts
only the bits that actually changed state.
Fixes: 2f94711b098b ("ext4: call ext4_mb_mark_context in ext4_mb_mark_diskspace_used")
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/mballoc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index ed1bd00e11cd..ff2023c9f52c 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -4228,7 +4228,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, handle_t *handle
ac->ac_b_ex.fe_group,
ac->ac_b_ex.fe_start,
ac->ac_b_ex.fe_len,
- 0, NULL);
+ EXT4_MB_BITMAP_MARKED_CHECK, NULL);
if (!err)
err = -EFSCORRUPTED;
return err;
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 01/17] lib/crc: add crc32c_flip_range() for incremental CRC update
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
When a contiguous range of bits in a buffer is flipped, the CRC32c
checksum can be updated incrementally without re-scanning the entire
buffer, by exploiting the linearity of CRCs over GF(2):
New_CRC = Old_CRC ^ CRC(flip_mask << trailing_bits)
Introduce crc32c_flip_range() which computes this delta using
precomputed GF(2) shift matrices and nibble-indexed lookup tables.
The implementation decomposes nbits and trailing_bits into
power-of-2 components and combines them via the CRC concatenation
property:
CRC(A || B) = shift(CRC(A), len(B)) ^ CRC(B)
This gives O(log N) complexity with only ~9.8KB of static tables
(fits in L1 cache). The current maximum supported buffer size is
64KB (INCR_MAX_ORDER = 19, i.e. 2^19 bits = 524288 bits = 64KB).
This is useful for filesystems like ext4, where bitmap updates
involve flipping a contiguous range of bits, and recalculating
the full CRC after every update is wasteful.
Benchmark results on Intel Xeon (Ice Lake) with CRC32c hardware
acceleration:
bitmap: 1024 2048 4096 8192 16384 32768 65536
flip(ns): 48 53 57 63 68 73 78
full(ns): 45 88 182 357 709 1421 2853
speedup: 0.9x 1.6x 3.1x 5.6x 10.3x 19.3x 36.3x
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
include/linux/crc32.h | 25 ++++++
lib/crc/.gitignore | 2 +
lib/crc/Makefile | 13 ++-
lib/crc/crc32c-incr.c | 140 +++++++++++++++++++++++++++++++
lib/crc/gen_crc32c_incr_table.c | 141 ++++++++++++++++++++++++++++++++
5 files changed, 318 insertions(+), 3 deletions(-)
create mode 100644 lib/crc/crc32c-incr.c
create mode 100644 lib/crc/gen_crc32c_incr_table.c
diff --git a/include/linux/crc32.h b/include/linux/crc32.h
index da78b215ff2e..034f73f0f5dc 100644
--- a/include/linux/crc32.h
+++ b/include/linux/crc32.h
@@ -81,6 +81,31 @@ u32 crc32_be(u32 crc, const void *p, size_t len);
*/
u32 crc32c(u32 crc, const void *p, size_t len);
+/**
+ * crc32c_flip_range - Update CRC32c after flipping a range of bits
+ * @old_crc: Existing CRC32c value of the buffer (pre-flip).
+ * @total_bits: Total size of the buffer in bits (e.g., 524288 for 64KB).
+ * @bit_off: Starting bit offset of the modified range.
+ * @nbits: Length of the flipped bit sequence.
+ *
+ * This function calculates the new CRC32c value when a contiguous range of
+ * bits is flipped (XORed with 1s) without re-scanning the entire buffer.
+ * It leverages the linearity of CRCs in Galois Field GF(2):
+ *
+ * New_CRC = Old_CRC ^ CRC(Mask_of_Ones << Trailing_Bits)
+ *
+ * The complexity is O(log nbits + log trailing_bits), making it
+ * significantly faster than recomputing the CRC for large buffers.
+ *
+ * Note: @total_bits must not exceed 524288 (2^19 bits = 64KB). Callers
+ * must ensure that @bit_off + @nbits <= @total_bits. Behavior is
+ * undefined if these constraints are violated.
+ *
+ * Return: The updated CRC32c value.
+ */
+u32 crc32c_flip_range(u32 old_crc, u32 total_bits,
+ u32 bit_off, u32 nbits);
+
/*
* crc32_optimizations() returns flags that indicate which CRC32 library
* functions are using architecture-specific optimizations. Unlike
diff --git a/lib/crc/.gitignore b/lib/crc/.gitignore
index a9e48103c9fb..4e2b9524426d 100644
--- a/lib/crc/.gitignore
+++ b/lib/crc/.gitignore
@@ -1,5 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
/crc32table.h
+/crc32c-incr-table.h
/crc64table.h
/gen_crc32table
+/gen_crc32c_incr_table
/gen_crc64table
diff --git a/lib/crc/Makefile b/lib/crc/Makefile
index ff213590e4e3..2c255ac029d0 100644
--- a/lib/crc/Makefile
+++ b/lib/crc/Makefile
@@ -21,7 +21,7 @@ crc-t10dif-$(CONFIG_X86) += x86/crc16-msb-pclmul.o
endif
obj-$(CONFIG_CRC32) += crc32.o
-crc32-y := crc32-main.o
+crc32-y := crc32-main.o crc32c-incr.o
ifeq ($(CONFIG_CRC32_ARCH),y)
CFLAGS_crc32-main.o += -I$(src)/$(SRCARCH)
crc32-$(CONFIG_ARM) += arm/crc32-core.o
@@ -49,20 +49,27 @@ endif # CONFIG_CRC64_ARCH
obj-y += tests/
-hostprogs := gen_crc32table gen_crc64table
-clean-files := crc32table.h crc64table.h
+hostprogs := gen_crc32table gen_crc32c_incr_table gen_crc64table
+clean-files := crc32table.h crc32c-incr-table.h crc64table.h
$(obj)/crc32-main.o: $(obj)/crc32table.h
+$(obj)/crc32c-incr.o: $(obj)/crc32c-incr-table.h
$(obj)/crc64-main.o: $(obj)/crc64table.h
quiet_cmd_crc32 = GEN $@
cmd_crc32 = $< > $@
+quiet_cmd_crc32c_incr = GEN $@
+ cmd_crc32c_incr = $< > $@
+
quiet_cmd_crc64 = GEN $@
cmd_crc64 = $< > $@
$(obj)/crc32table.h: $(obj)/gen_crc32table
$(call cmd,crc32)
+$(obj)/crc32c-incr-table.h: $(obj)/gen_crc32c_incr_table
+ $(call cmd,crc32c_incr)
+
$(obj)/crc64table.h: $(obj)/gen_crc64table
$(call cmd,crc64)
diff --git a/lib/crc/crc32c-incr.c b/lib/crc/crc32c-incr.c
new file mode 100644
index 000000000000..b6258231cc0d
--- /dev/null
+++ b/lib/crc/crc32c-incr.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * GF(2) matrix-based CRC32c incremental update.
+ *
+ * When a contiguous range of bits is flipped, the new CRC can be
+ * derived from the old one without re-scanning the buffer:
+ * New_CRC = Old_CRC ^ CRC(flip_mask << trailing_bits)
+ *
+ * The delta CRC is computed by decomposing num_bits and trailing_bits
+ * into power-of-2 components and combining them via the CRC
+ * concatenation property, giving O(log N) complexity.
+ *
+ * Memory usage: ~9.8KB
+ * - crc32c_incr_nibble_table: 19 * 8 * 16 * 4 = 9728 bytes
+ * - crc32c_incr_ones_lookup: 20 * 4 = 80 bytes
+ *
+ * Tables are generated at compile time by gen_crc32c_incr_table.
+ * INCR_MAX_ORDER 19 supports up to 64KB buffers (2^19 bits).
+ *
+ * Copyright (C) 2026 Alibaba Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/export.h>
+#include <linux/crc32.h>
+
+#include "crc32c-incr-table.h"
+
+#define INCR_MAX_ORDER 19
+
+/**
+ * gf2_xform - Multiply a CRC state vector by a GF(2) shift matrix
+ * @order: Selects the precomputed matrix M^(2^order).
+ * @v: The 32-bit CRC state vector.
+ *
+ * Computes v * M^(2^order) using nibble (4-bit) indexed tables,
+ * reducing the operation from 32 bit-level iterations to 8 lookups.
+ */
+static inline u32 gf2_xform(int order, u32 v)
+{
+ const u32 (*tab)[16] = crc32c_incr_nibble_table[order];
+
+ return tab[0][v & 0xf] ^
+ tab[1][(v >> 4) & 0xf] ^
+ tab[2][(v >> 8) & 0xf] ^
+ tab[3][(v >> 12) & 0xf] ^
+ tab[4][(v >> 16) & 0xf] ^
+ tab[5][(v >> 20) & 0xf] ^
+ tab[6][(v >> 24) & 0xf] ^
+ tab[7][(v >> 28) & 0xf];
+}
+
+/**
+ * crc32c_incr_get_ones_delta - Compute CRC of an all-ones bit sequence
+ * @num_bits: Length of the all-ones sequence.
+ *
+ * Returns CRC(0, [111...1] of length num_bits). Decomposes num_bits
+ * into powers of 2 (MSB-first) and combines using:
+ * CRC(A || B) = shift(CRC(A), len(B)) ^ CRC(B)
+ *
+ * This requires only (popcount - 1) gf2_xform calls, each doing
+ * 8 table lookups.
+ *
+ * Caller must ensure num_bits <= (1UL << INCR_MAX_ORDER).
+ */
+static u32 crc32c_incr_get_ones_delta(size_t num_bits)
+{
+ u32 delta;
+ int n;
+
+ if (!num_bits)
+ return 0;
+
+ /* Initialize with the highest power-of-2 block */
+ n = __fls(num_bits);
+ delta = crc32c_incr_ones_lookup[n];
+ num_bits ^= (1UL << n);
+
+ /* Concatenate remaining blocks from high to low */
+ while (num_bits) {
+ n = __fls(num_bits);
+ delta = gf2_xform(n, delta);
+ delta ^= crc32c_incr_ones_lookup[n];
+ num_bits ^= (1UL << n);
+ }
+ return delta;
+}
+
+/**
+ * gf2_shift_crc - Shift a CRC state by @trailing_bits zero-bit positions
+ * @crc: The CRC state vector.
+ * @trailing_bits: Number of zero bits to shift through.
+ *
+ * Equivalent to appending @trailing_bits zero bits to the data stream
+ * and continuing the CRC computation. Decomposes trailing_bits into
+ * powers of 2 and applies the corresponding precomputed matrices.
+ */
+static u32 gf2_shift_crc(u32 crc, size_t trailing_bits)
+{
+ int n;
+
+ for (n = 0; trailing_bits > 0 && n < INCR_MAX_ORDER; n++) {
+ if (trailing_bits & 1)
+ crc = gf2_xform(n, crc);
+ trailing_bits >>= 1;
+ }
+ return crc;
+}
+
+/* See full kernel-doc in include/linux/crc32.h */
+u32 crc32c_flip_range(u32 old_crc, u32 total_bits,
+ u32 bit_off, u32 nbits)
+{
+ u32 delta, trailing_bits;
+
+ if (!nbits)
+ return old_crc;
+
+ /*
+ * total_bits must not exceed 2^INCR_MAX_ORDER bits (64KB).
+ * bit_off + nbits must not exceed total_bits.
+ */
+ if (WARN_ON_ONCE(total_bits > (1UL << INCR_MAX_ORDER)))
+ return old_crc;
+ if (WARN_ON_ONCE(bit_off + nbits > total_bits))
+ return old_crc;
+
+ trailing_bits = total_bits - (bit_off + nbits);
+
+ /* 1. Calculate CRC of the flip-mask (all 1s of length nbits) */
+ delta = crc32c_incr_get_ones_delta(nbits);
+
+ /* 2. Shift the mask-CRC to the correct bit position */
+ delta = gf2_shift_crc(delta, trailing_bits);
+
+ /* 3. Apply the delta to the existing CRC */
+ return old_crc ^ delta;
+}
+EXPORT_SYMBOL(crc32c_flip_range);
diff --git a/lib/crc/gen_crc32c_incr_table.c b/lib/crc/gen_crc32c_incr_table.c
new file mode 100644
index 000000000000..f906506282cc
--- /dev/null
+++ b/lib/crc/gen_crc32c_incr_table.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generate GF(2) nibble-based lookup tables for incremental CRC32c updates.
+ * MAX_ORDER 19 supports up to 64KB buffers (2^19 bits = 524288 bits).
+ *
+ * Instead of storing raw 32x32 bit matrices (32 rows per order),
+ * we precompute nibble (4-bit) indexed tables. This reduces gf2_xform
+ * to 8 table lookups instead of 32 branchless mask-and-XOR iterations.
+ *
+ * Memory layout:
+ * - crc32c_incr_nibble_table[19][8][16]: 19 * 8 * 16 * 4 = 9728 bytes
+ * - crc32c_incr_ones_lookup[20]: 20 * 4 = 80 bytes
+ * Total: ~9.8KB (fits comfortably in L1 cache)
+ *
+ * Copyright (C) 2026 Alibaba Inc.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "../../include/linux/crc32poly.h"
+
+#define CRC32C_INCR_MAX_ORDER 19
+#define NIBBLES_PER_U32 8
+
+static uint32_t bit_matrix[CRC32C_INCR_MAX_ORDER][32];
+static uint32_t nibble_table[CRC32C_INCR_MAX_ORDER][NIBBLES_PER_U32][16];
+static uint32_t ones_lookup[CRC32C_INCR_MAX_ORDER + 1];
+
+static void crc32c_incr_init(void)
+{
+ int n, i, k, v;
+
+ /*
+ * Step 1: Build the order-0 matrix M, where M[i] is the CRC
+ * state after shifting basis vector e_i by one bit position.
+ */
+ for (i = 0; i < 32; i++) {
+ uint32_t r = 1U << i;
+
+ bit_matrix[0][i] = (r & 1) ?
+ (r >> 1) ^ CRC32C_POLY_LE : (r >> 1);
+ }
+
+ /* Step 2: M^(2^n) = (M^(2^(n-1)))^2 via matrix squaring */
+ for (n = 1; n < CRC32C_INCR_MAX_ORDER; n++) {
+ for (i = 0; i < 32; i++) {
+ uint32_t r = bit_matrix[n - 1][i];
+ uint32_t res = 0;
+
+ for (k = 0; k < 32; k++) {
+ if (r & (1U << k))
+ res ^= bit_matrix[n - 1][k];
+ }
+ bit_matrix[n][i] = res;
+ }
+ }
+
+ /* Step 3: Convert bit matrices to nibble-indexed lookup tables */
+ for (n = 0; n < CRC32C_INCR_MAX_ORDER; n++) {
+ for (i = 0; i < NIBBLES_PER_U32; i++) {
+ nibble_table[n][i][0] = 0;
+ for (v = 1; v < 16; v++) {
+ uint32_t res = 0;
+
+ for (k = 0; k < 4; k++) {
+ if (v & (1 << k))
+ res ^= bit_matrix[n][i * 4 + k];
+ }
+ nibble_table[n][i][v] = res;
+ }
+ }
+ }
+
+ /*
+ * Step 4: ones_lookup[n] = CRC(0, all-ones of 2^n bits).
+ * Uses CRC(A||B) = shift(CRC(A), len(B)) ^ CRC(B) to double
+ * the length at each step. ones_lookup[0] = CRC of a single
+ * 1-bit, which equals the generator polynomial.
+ */
+ ones_lookup[0] = CRC32C_POLY_LE;
+
+ for (n = 1; n <= CRC32C_INCR_MAX_ORDER; n++) {
+ uint32_t low = ones_lookup[n - 1];
+ uint32_t high = 0;
+
+ for (k = 0; k < 32; k++) {
+ if (low & (1U << k))
+ high ^= bit_matrix[n - 1][k];
+ }
+ ones_lookup[n] = low ^ high;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int n, i, v;
+
+ crc32c_incr_init();
+
+ printf("/* this file is generated - do not edit */\n\n");
+
+ printf("static const u32 crc32c_incr_nibble_table[%d][%d][16] = {\n",
+ CRC32C_INCR_MAX_ORDER, NIBBLES_PER_U32);
+ for (n = 0; n < CRC32C_INCR_MAX_ORDER; n++) {
+ printf("\t{\n");
+ for (i = 0; i < NIBBLES_PER_U32; i++) {
+ printf("\t\t{\n");
+ for (v = 0; v < 16; v += 4) {
+ printf("\t\t\t0x%08x, 0x%08x, 0x%08x, 0x%08x,\n",
+ nibble_table[n][i][v],
+ nibble_table[n][i][v + 1],
+ nibble_table[n][i][v + 2],
+ nibble_table[n][i][v + 3]);
+ }
+ printf("\t\t},\n");
+ }
+ printf("\t},\n");
+ }
+ printf("};\n\n");
+
+ printf("static const u32 crc32c_incr_ones_lookup[%d] = {\n",
+ CRC32C_INCR_MAX_ORDER + 1);
+ for (n = 0; n <= CRC32C_INCR_MAX_ORDER; n += 4) {
+ int remaining = CRC32C_INCR_MAX_ORDER + 1 - n;
+
+ if (remaining >= 4) {
+ printf("\t0x%08x, 0x%08x, 0x%08x, 0x%08x,\n",
+ ones_lookup[n], ones_lookup[n + 1],
+ ones_lookup[n + 2], ones_lookup[n + 3]);
+ } else {
+ printf("\t");
+ for (i = 0; i < remaining; i++)
+ printf("0x%08x, ", ones_lookup[n + i]);
+ printf("\n");
+ }
+ }
+ printf("};\n");
+
+ return 0;
+}
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 02/17] lib/crc: crc_kunit: add kunit test for crc32c_flip_range()
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Add kunit tests for crc32c_flip_range(), validating correctness
against naive full-buffer CRC recomputation. All tests use a 64KB
buffer and a non-zero CRC seed to match real-world usage (e.g. ext4
metadata checksums):
- ones_lookup[0] single-bit verification.
- num_bits=0 no-op, first/last byte, full 64KB flip.
- Random single-bit flips (100 iterations).
- Random multi-bit contiguous ranges (100 iterations).
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
lib/crc/tests/crc_kunit.c | 85 +++++++++++++++++++++++++++++++++++++++
1 file changed, 85 insertions(+)
diff --git a/lib/crc/tests/crc_kunit.c b/lib/crc/tests/crc_kunit.c
index 9428cd913625..46f9df5b58e4 100644
--- a/lib/crc/tests/crc_kunit.c
+++ b/lib/crc/tests/crc_kunit.c
@@ -470,6 +470,90 @@ static void crc64_nvme_benchmark(struct kunit *test)
}
#endif /* CONFIG_CRC64 */
+/*
+ * Test crc32c_flip_range() against naive full-buffer CRC recomputation.
+ * All tests use a 64KB buffer (2^19 bits = INCR_MAX_ORDER limit)
+ * and a non-zero seed to match real-world usage (e.g. ext4 checksums).
+ */
+static void crc32c_flip_range_test(struct kunit *test)
+{
+ size_t buflen = 65536;
+ size_t total_bits = buflen * 8;
+ u32 seed = 0x12345678;
+ u32 expected, flip_crc;
+ size_t start, num_bits, b, pos;
+ u8 *buf;
+ int i;
+
+ buf = kunit_kmalloc(test, buflen, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+
+ /* Test 1: Single bit at bit 0 (verifies ones_lookup[0]) */
+ buf[0] = 0x00;
+ expected = crc32c(seed, buf, 1);
+ buf[0] = 0x01;
+ flip_crc = crc32c_flip_range(expected, 8, 0, 1);
+ expected = crc32c(seed, buf, 1);
+ KUNIT_ASSERT_EQ_MSG(test, expected, flip_crc, "Single bit at bit 0");
+
+ /* Test 2: num_bits=0 should be a no-op */
+ memset(buf, 0, buflen);
+ expected = crc32c(seed, buf, buflen);
+ flip_crc = crc32c_flip_range(expected, total_bits, 0, 0);
+ KUNIT_ASSERT_EQ_MSG(test, expected, flip_crc,
+ "num_bits=0: expected=0x%08x got=0x%08x",
+ expected, flip_crc);
+
+ /* Test 3: Boundary flips - first byte, last byte, all bits */
+ buf[0] = 0xFF;
+ flip_crc = crc32c_flip_range(expected, total_bits, 0, 8);
+ expected = crc32c(seed, buf, buflen);
+ KUNIT_ASSERT_EQ_MSG(test, expected, flip_crc, "Flip first byte");
+
+ buf[buflen - 1] = 0xFF;
+ flip_crc = crc32c_flip_range(expected, total_bits, (buflen - 1) * 8, 8);
+ expected = crc32c(seed, buf, buflen);
+ KUNIT_ASSERT_EQ_MSG(test, expected, flip_crc, "Flip last byte");
+
+ memset(buf, 0, buflen);
+ expected = crc32c(seed, buf, buflen);
+ memset(buf, 0xFF, buflen);
+ flip_crc = crc32c_flip_range(expected, total_bits, 0, total_bits);
+ expected = crc32c(seed, buf, buflen);
+ KUNIT_ASSERT_EQ_MSG(test, expected, flip_crc, "Flip all 64KB bits");
+
+ /* Test 4: Random single-bit flips (100 iterations) */
+ memset(buf, 0, buflen);
+ expected = crc32c(seed, buf, buflen);
+ for (i = 0; i < 100; i++) {
+ start = rand32() % total_bits;
+ buf[start / 8] ^= (1 << (start % 8));
+
+ flip_crc = crc32c_flip_range(expected, total_bits, start, 1);
+ expected = crc32c(seed, buf, buflen);
+ KUNIT_ASSERT_EQ_MSG(test, expected, flip_crc,
+ "Single bit at %zu: expected=0x%08x got=0x%08x",
+ start, expected, flip_crc);
+ }
+
+ /* Test 5: Random multi-bit ranges (100 iterations) */
+ for (i = 0; i < 100; i++) {
+ num_bits = (rand32() % (total_bits - 1)) + 1;
+ start = rand32() % (total_bits - num_bits + 1);
+ for (b = 0; b < num_bits; b++) {
+ pos = start + b;
+ buf[pos / 8] ^= (1 << (pos % 8));
+ }
+
+ flip_crc = crc32c_flip_range(expected, total_bits, start, num_bits);
+ expected = crc32c(seed, buf, buflen);
+
+ KUNIT_ASSERT_EQ_MSG(test, expected, flip_crc,
+ "Range [%zu, +%zu): expected=0x%08x got=0x%08x",
+ start, num_bits, expected, flip_crc);
+ }
+}
+
static struct kunit_case crc_test_cases[] = {
#if IS_REACHABLE(CONFIG_CRC7)
KUNIT_CASE(crc7_be_test),
@@ -490,6 +574,7 @@ static struct kunit_case crc_test_cases[] = {
KUNIT_CASE(crc32_be_benchmark),
KUNIT_CASE(crc32c_test),
KUNIT_CASE(crc32c_benchmark),
+ KUNIT_CASE(crc32c_flip_range_test),
#endif
#if IS_REACHABLE(CONFIG_CRC64)
KUNIT_CASE(crc64_be_test),
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 03/17] lib/crc: crc_kunit: add benchmark for crc32c_flip_range()
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Add a kunit benchmark comparing crc32c_flip_range() against full crc32c
recomputation across bitmap sizes from 1KB to 64KB. The benchmark reports
per-call latency in nanoseconds and the speedup ratio.
Sample results (x86_64, Intel(R) Xeon(R) Platinum 8331C):
bitmap=1024: flip_range=48 ns, full_crc=45 ns, speedup=0.9x
bitmap=2048: flip_range=53 ns, full_crc=88 ns, speedup=1.6x
bitmap=4096: flip_range=57 ns, full_crc=182 ns, speedup=3.1x
bitmap=8192: flip_range=63 ns, full_crc=357 ns, speedup=5.6x
bitmap=16384: flip_range=68 ns, full_crc=709 ns, speedup=10.3x
bitmap=32768: flip_range=73 ns, full_crc=1421 ns, speedup=19.3x
bitmap=65536: flip_range=78 ns, full_crc=2853 ns, speedup=36.3x
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
lib/crc/tests/crc_kunit.c | 52 +++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/lib/crc/tests/crc_kunit.c b/lib/crc/tests/crc_kunit.c
index 46f9df5b58e4..8e8b541b37d3 100644
--- a/lib/crc/tests/crc_kunit.c
+++ b/lib/crc/tests/crc_kunit.c
@@ -554,6 +554,57 @@ static void crc32c_flip_range_test(struct kunit *test)
}
}
+/*
+ * Benchmark crc32c_flip_range vs full crc32c recomputation
+ */
+static void crc32c_flip_range_benchmark(struct kunit *test)
+{
+ static const size_t bitmap_sizes[] = {
+ 1024, 2048, 4096, 8192, 16384, 32768, 65536,
+ };
+ size_t i, j, num_iters, buflen, total_bits;
+ volatile u32 crc;
+ u64 t_flip, t_full;
+ u8 *buf;
+
+ if (!IS_ENABLED(CONFIG_CRC_BENCHMARK))
+ kunit_skip(test, "not enabled");
+
+ buf = kunit_kzalloc(test, 65536, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+
+ for (i = 0; i < ARRAY_SIZE(bitmap_sizes); i++) {
+ buflen = bitmap_sizes[i];
+ total_bits = buflen * 8;
+ num_iters = 10000000 / (buflen + 128);
+
+ /* Benchmark crc32c_flip_range */
+ crc = crc32c(0, buf, buflen);
+ preempt_disable();
+ t_flip = ktime_get_ns();
+ for (j = 0; j < num_iters; j++)
+ crc = crc32c_flip_range(crc, total_bits, 100, 100);
+ t_flip = ktime_get_ns() - t_flip;
+ preempt_enable();
+
+ /* Benchmark full crc32c recomputation */
+ preempt_disable();
+ t_full = ktime_get_ns();
+ for (j = 0; j < num_iters; j++)
+ crc = crc32c(0, buf, buflen);
+ t_full = ktime_get_ns() - t_full;
+ preempt_enable();
+
+ kunit_info(test,
+ "bitmap=%zu: flip_range=%llu ns, full_crc=%llu ns, speedup=%llu.%01llux\n",
+ buflen,
+ div64_u64(t_flip, num_iters),
+ div64_u64(t_full, num_iters),
+ div64_u64(t_full * 10, t_flip ? t_flip : 1) / 10,
+ div64_u64(t_full * 10, t_flip ? t_flip : 1) % 10);
+ }
+}
+
static struct kunit_case crc_test_cases[] = {
#if IS_REACHABLE(CONFIG_CRC7)
KUNIT_CASE(crc7_be_test),
@@ -575,6 +626,7 @@ static struct kunit_case crc_test_cases[] = {
KUNIT_CASE(crc32c_test),
KUNIT_CASE(crc32c_benchmark),
KUNIT_CASE(crc32c_flip_range_test),
+ KUNIT_CASE(crc32c_flip_range_benchmark),
#endif
#if IS_REACHABLE(CONFIG_CRC64)
KUNIT_CASE(crc64_be_test),
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 06/17] ext4: add ext4_block_bitmap_csum_set_range() for incremental checksum update
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Add a helper function ext4_block_bitmap_csum_set_range() that updates
the block bitmap checksum using crc32c_flip_range() for incremental CRC
calculation. Unlike ext4_block_bitmap_csum_set() which re-scans the
entire bitmap buffer, this function efficiently computes the CRC delta
for a range of flipped bits, avoiding the cost of a full CRC
recalculation.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/bitmap.c | 24 ++++++++++++++++++++++++
fs/ext4/ext4.h | 3 +++
2 files changed, 27 insertions(+)
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
index 46affc9e80ca..00b0a3c74859 100644
--- a/fs/ext4/bitmap.c
+++ b/fs/ext4/bitmap.c
@@ -110,3 +110,27 @@ void ext4_block_bitmap_csum_set(struct super_block *sb,
csum = ext4_chksum(sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
ext4_block_bitmap_csum_store(sb, gdp, csum);
}
+
+/*
+ * Update block bitmap checksum using incremental CRC calculation.
+ *
+ * This function assumes that ALL bits in the range [offset, offset+len)
+ * have been flipped (XORed with 1). It uses crc32c_flip_range() to
+ * efficiently compute the CRC delta without re-scanning the entire bitmap.
+ * The csum_seed cancels out in the XOR delta, so it is not needed here.
+ */
+void ext4_block_bitmap_csum_set_range(struct super_block *sb,
+ struct ext4_group_desc *gdp,
+ ext4_grpblk_t offset, ext4_grpblk_t len)
+{
+ __u32 new_csum, old_csum;
+
+ if (!ext4_has_feature_metadata_csum(sb))
+ return;
+
+ old_csum = ext4_block_bitmap_csum_get(sb, gdp);
+ new_csum = crc32c_flip_range(old_csum, EXT4_CLUSTERS_PER_GROUP(sb),
+ offset, len);
+
+ ext4_block_bitmap_csum_store(sb, gdp, new_csum);
+}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 94283a991e5c..c423a9a04047 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2770,6 +2770,9 @@ int ext4_inode_bitmap_csum_verify(struct super_block *sb,
void ext4_block_bitmap_csum_set(struct super_block *sb,
struct ext4_group_desc *gdp,
struct buffer_head *bh);
+void ext4_block_bitmap_csum_set_range(struct super_block *sb,
+ struct ext4_group_desc *gdp,
+ ext4_grpblk_t offset, ext4_grpblk_t len);
int ext4_block_bitmap_csum_verify(struct super_block *sb,
struct ext4_group_desc *gdp,
struct buffer_head *bh);
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 05/17] ext4: extract block bitmap checksum get and store helpers
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Add ext4_block_bitmap_csum_get() and ext4_block_bitmap_csum_store()
helpers, and use EXT4_DESC_SIZE(sb) instead of sbi->s_desc_size for
consistency. No functional change.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/bitmap.c | 31 ++++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
index 87760fabdd2e..46affc9e80ca 100644
--- a/fs/ext4/bitmap.c
+++ b/fs/ext4/bitmap.c
@@ -58,11 +58,29 @@ void ext4_inode_bitmap_csum_set(struct super_block *sb,
gdp->bg_inode_bitmap_csum_hi = cpu_to_le16(csum >> 16);
}
+static inline __u32 ext4_block_bitmap_csum_get(struct super_block *sb,
+ struct ext4_group_desc *gdp)
+{
+ __u32 csum = le16_to_cpu(gdp->bg_block_bitmap_csum_lo);
+
+ if (EXT4_DESC_SIZE(sb) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END)
+ csum |= (__u32)le16_to_cpu(gdp->bg_block_bitmap_csum_hi) << 16;
+ return csum;
+}
+
+static inline void ext4_block_bitmap_csum_store(struct super_block *sb,
+ struct ext4_group_desc *gdp,
+ __u32 csum)
+{
+ gdp->bg_block_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF);
+ if (EXT4_DESC_SIZE(sb) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END)
+ gdp->bg_block_bitmap_csum_hi = cpu_to_le16(csum >> 16);
+}
+
int ext4_block_bitmap_csum_verify(struct super_block *sb,
struct ext4_group_desc *gdp,
struct buffer_head *bh)
{
- __u32 hi;
__u32 provided, calculated;
struct ext4_sb_info *sbi = EXT4_SB(sb);
int sz = EXT4_CLUSTERS_PER_GROUP(sb) / 8;
@@ -70,12 +88,9 @@ int ext4_block_bitmap_csum_verify(struct super_block *sb,
if (!ext4_has_feature_metadata_csum(sb))
return 1;
- provided = le16_to_cpu(gdp->bg_block_bitmap_csum_lo);
+ provided = ext4_block_bitmap_csum_get(sb, gdp);
calculated = ext4_chksum(sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
- if (sbi->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END) {
- hi = le16_to_cpu(gdp->bg_block_bitmap_csum_hi);
- provided |= (hi << 16);
- } else
+ if (EXT4_DESC_SIZE(sb) < EXT4_BG_BLOCK_BITMAP_CSUM_HI_END)
calculated &= 0xFFFF;
return provided == calculated;
@@ -93,7 +108,5 @@ void ext4_block_bitmap_csum_set(struct super_block *sb,
return;
csum = ext4_chksum(sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
- gdp->bg_block_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF);
- if (sbi->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END)
- gdp->bg_block_bitmap_csum_hi = cpu_to_le16(csum >> 16);
+ ext4_block_bitmap_csum_store(sb, gdp, csum);
}
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 07/17] ext4: use fast incremental CRC update in ext4_mb_mark_context()
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Use ext4_block_bitmap_csum_set_range() in ext4_mb_mark_context() for
fast incremental block bitmap checksum updates. Instead of re-scanning
the entire bitmap after every allocation or free, the incremental update
computes the CRC delta for the modified bit range in O(log N) time.
Add a fast_crc flag that is set when EXT4_MB_BITMAP_MARKED_CHECK is not
used. When fast_crc is true, all bits in the range are guaranteed to flip,
so the incremental CRC via ext4_block_bitmap_csum_set_range() is correct.
Otherwise, fall back to ext4_block_bitmap_csum_set() for a full CRC
recalculation, since idempotent operations (mb_set_bits/mb_clear_bits
with EXT4_MB_BITMAP_MARKED_CHECK) may leave some bits unchanged.
For the BLOCK_UNINIT case, the bitmap was just initialized and there is
no valid old checksum, so fast_crc is forced to false to ensure a full
CRC recalculation establishes a correct baseline.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/mballoc.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index ff2023c9f52c..77f6309916d1 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -4095,6 +4095,7 @@ ext4_mb_mark_context(handle_t *handle, struct super_block *sb, bool state,
struct buffer_head *gdp_bh;
int err;
unsigned int i, already, changed = len;
+ bool fast_crc;
KUNIT_STATIC_STUB_REDIRECT(ext4_mb_mark_context,
handle, sb, state, group, blkoff, len,
@@ -4127,12 +4128,28 @@ ext4_mb_mark_context(handle_t *handle, struct super_block *sb, bool state,
goto out_err;
}
+ /*
+ * fast_crc: Use incremental CRC update via crc32c_flip_range().
+ * This is only valid when all bits in [blkoff, blkoff+len) are
+ * guaranteed to be in the opposite state (i.e., every bit will
+ * actually flip). When EXT4_MB_BITMAP_MARKED_CHECK is set,
+ * mb_set_bits/mb_clear_bits are idempotent, so some bits may not
+ * change and incremental CRC would produce incorrect results.
+ */
+ fast_crc = !(flags & EXT4_MB_BITMAP_MARKED_CHECK);
+
ext4_lock_group(sb, group);
if (ext4_has_group_desc_csum(sb) &&
(gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
ext4_free_group_clusters_set(sb, gdp,
ext4_free_clusters_after_init(sb, group, gdp));
+ /*
+ * The bitmap was just initialized, so the old checksum
+ * is invalid for incremental CRC update. Fall back to
+ * full recalculation.
+ */
+ fast_crc = false;
}
if (flags & EXT4_MB_BITMAP_MARKED_CHECK) {
@@ -4154,7 +4171,10 @@ ext4_mb_mark_context(handle_t *handle, struct super_block *sb, bool state,
ext4_free_group_clusters(sb, gdp) + changed);
}
- ext4_block_bitmap_csum_set(sb, gdp, bitmap_bh);
+ if (fast_crc)
+ ext4_block_bitmap_csum_set_range(sb, gdp, blkoff, len);
+ else
+ ext4_block_bitmap_csum_set(sb, gdp, bitmap_bh);
ext4_group_desc_csum_set(sb, group, gdp);
ext4_unlock_group(sb, group);
if (ret_changed)
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 14/17] ext4: rename ino to bit in __ext4_new_inode()
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
In __ext4_new_inode(), the variable 'ino' actually holds a zero-based
bit position within the inode bitmap, not an absolute inode number.
Rename it to 'bit' to better reflect its semantics and avoid confusion
with inode->i_ino.
With this rename, the previous 'ino++' before calculating i_ino is no
longer needed; instead compute i_ino directly as 'bit + 1'.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/ialloc.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index e209e27f827f..8b75b331b26e 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -974,7 +974,7 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
struct buffer_head *inode_bitmap_bh = NULL;
struct buffer_head *group_desc_bh;
ext4_group_t ngroups, group = 0;
- unsigned long ino = 0;
+ unsigned long bit = 0;
struct inode *inode;
struct ext4_group_desc *gdp = NULL;
struct ext4_inode_info *ei;
@@ -1050,7 +1050,7 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
if (goal && goal <= le32_to_cpu(sbi->s_es->s_inodes_count)) {
group = (goal - 1) / EXT4_INODES_PER_GROUP(sb);
- ino = (goal - 1) % EXT4_INODES_PER_GROUP(sb);
+ bit = (goal - 1) % EXT4_INODES_PER_GROUP(sb);
ret2 = 0;
goto got_group;
}
@@ -1071,7 +1071,7 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
* unless we get unlucky and it turns out the group we selected
* had its last inode grabbed by someone else.
*/
- for (i = 0; i < ngroups; i++, ino = 0) {
+ for (i = 0; i < ngroups; i++, bit = 0) {
err = -EIO;
gdp = ext4_get_group_desc(sb, group, &group_desc_bh);
@@ -1105,13 +1105,13 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
EXT4_MB_GRP_IBITMAP_CORRUPT(grp))
goto next_group;
- ret2 = find_inode_bit(sb, group, inode_bitmap_bh, &ino);
+ ret2 = find_inode_bit(sb, group, inode_bitmap_bh, &bit);
if (!ret2)
goto next_group;
- if (group == 0 && (ino + 1) < EXT4_FIRST_INO(sb)) {
+ if (group == 0 && (bit + 1) < EXT4_FIRST_INO(sb)) {
ext4_error(sb, "reserved inode found cleared - "
- "inode=%lu", ino + 1);
+ "inode=%lu", bit + 1);
ext4_mark_group_bitmap_corrupted(sb, group,
EXT4_GROUP_INFO_IBITMAP_CORRUPT);
goto next_group;
@@ -1136,21 +1136,20 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
goto out;
}
ext4_lock_group(sb, group);
- ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);
+ ret2 = ext4_test_and_set_bit(bit, inode_bitmap_bh->b_data);
if (ret2) {
/* Someone already took the bit. Repeat the search
* with lock held.
*/
- ret2 = find_inode_bit(sb, group, inode_bitmap_bh, &ino);
+ ret2 = find_inode_bit(sb, group, inode_bitmap_bh, &bit);
if (ret2) {
- ext4_set_bit(ino, inode_bitmap_bh->b_data);
+ ext4_set_bit(bit, inode_bitmap_bh->b_data);
ret2 = 0;
} else {
ret2 = 1; /* we didn't grab the inode */
}
}
ext4_unlock_group(sb, group);
- ino++; /* the inode bitmap is zero-based */
if (!ret2)
goto got; /* we grabbed the inode! */
@@ -1210,9 +1209,9 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
* relative inode number in this group. if it is greater
* we need to update the bg_itable_unused count
*/
- if (ino > free)
+ if (bit >= free)
ext4_itable_unused_set(sb, gdp,
- (EXT4_INODES_PER_GROUP(sb) - ino));
+ (EXT4_INODES_PER_GROUP(sb) - bit - 1));
if (!(sbi->s_mount_state & EXT4_FC_REPLAY))
up_read(&grp->alloc_sem);
} else {
@@ -1252,7 +1251,8 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
flex_group)->free_inodes);
}
- inode->i_ino = ino + group * EXT4_INODES_PER_GROUP(sb);
+ /* the inode bitmap is zero-based */
+ inode->i_ino = bit + 1 + group * EXT4_INODES_PER_GROUP(sb);
/* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = 0;
simple_inode_init_ts(inode);
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 16/17] ext4: extract ext4_update_inode_group_desc() to reduce duplication
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
ext4_mark_inode_used() and __ext4_new_inode() contain nearly
identical code blocks for updating group descriptor fields after
inode allocation (UNINIT flag clearing, itable_unused update,
inode bitmap checksum, group desc checksum). Extract the common
logic into ext4_update_inode_group_desc() to eliminate duplication.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/ialloc.c | 136 ++++++++++++++++++++---------------------------
1 file changed, 57 insertions(+), 79 deletions(-)
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 9dd1cdb367ba..25430c572818 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -808,12 +808,63 @@ static int ext4_might_init_block_bitmap(handle_t *handle,
return err;
}
+/*
+ * Update group descriptor checksums and itable_unused after allocating
+ * inode @bit (0-based relative inode number within the group).
+ * Must be called with the group lock held.
+ */
+static void ext4_update_inode_group_desc(struct super_block *sb,
+ ext4_group_t group,
+ struct ext4_group_desc *gdp,
+ struct buffer_head *inode_bitmap_bh,
+ int bit, umode_t mode)
+{
+ int free;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ bool fast_crc = true;
+
+ ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
+ if (S_ISDIR(mode)) {
+ ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
+ if (sbi->s_log_groups_per_flex) {
+ ext4_group_t f = ext4_flex_group(sbi, group);
+
+ atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
+ f)->used_dirs);
+ }
+ }
+
+ if (!ext4_has_group_desc_csum(sb))
+ return;
+
+ free = EXT4_INODES_PER_GROUP(sb) - ext4_itable_unused_count(sb, gdp);
+ if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
+ gdp->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT);
+ free = 0;
+ /* Incremental CRC needs a valid checksum baseline */
+ fast_crc = false;
+ }
+
+ /*
+ * Check the relative inode number against the last used
+ * relative inode number in this group. If it is greater
+ * we need to update the bg_itable_unused count.
+ */
+ if (bit >= free)
+ ext4_itable_unused_set(sb, gdp,
+ EXT4_INODES_PER_GROUP(sb) - bit - 1);
+ if (fast_crc)
+ ext4_inode_bitmap_csum_set_fast(sb, gdp, bit);
+ else
+ ext4_inode_bitmap_csum_set(sb, gdp, inode_bitmap_bh);
+ ext4_group_desc_csum_set(sb, group, gdp);
+}
+
int ext4_mark_inode_used(struct super_block *sb, int ino, umode_t mode)
{
unsigned long max_ino = le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count);
struct buffer_head *inode_bitmap_bh = NULL, *group_desc_bh = NULL;
struct ext4_group_desc *gdp;
- struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_group_t group;
int bit;
int err;
@@ -848,44 +899,8 @@ int ext4_mark_inode_used(struct super_block *sb, int ino, umode_t mode)
ext4_set_bit(bit, inode_bitmap_bh->b_data);
/* Update the relevant bg descriptor fields */
- ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
- if (S_ISDIR(mode)) {
- ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
- if (sbi->s_log_groups_per_flex) {
- ext4_group_t f = ext4_flex_group(sbi, group);
-
- atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
- f)->used_dirs);
- }
- }
-
- if (ext4_has_group_desc_csum(sb)) {
- bool fast_crc = true;
- int free = EXT4_INODES_PER_GROUP(sb) -
- ext4_itable_unused_count(sb, gdp);
-
- if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
- gdp->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT);
- free = 0;
- /* Incremental CRC needs a valid checksum baseline */
- fast_crc = false;
- }
-
- /*
- * Check the relative inode number against the last used
- * relative inode number in this group. if it is greater
- * we need to update the bg_itable_unused count
- */
- if (bit >= free)
- ext4_itable_unused_set(sb, gdp,
- (EXT4_INODES_PER_GROUP(sb) - bit - 1));
- if (fast_crc)
- ext4_inode_bitmap_csum_set_fast(sb, gdp, bit);
- else
- ext4_inode_bitmap_csum_set(sb, gdp, inode_bitmap_bh);
- ext4_group_desc_csum_set(sb, group, gdp);
- }
-
+ ext4_update_inode_group_desc(sb, group, gdp,
+ inode_bitmap_bh, bit, mode);
ext4_unlock_group(sb, group);
BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata");
@@ -1165,50 +1180,13 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
ret2 = 0;
} else {
ret2 = 1; /* we didn't grab the inode */
- goto unlock_group;
}
}
/* Update the relevant bg descriptor fields */
- ext4_free_inodes_set(sb, gdp,
- ext4_free_inodes_count(sb, gdp) - 1);
- if (S_ISDIR(mode)) {
- ext4_used_dirs_set(sb, gdp,
- ext4_used_dirs_count(sb, gdp) + 1);
- if (sbi->s_log_groups_per_flex) {
- ext4_group_t f = ext4_flex_group(sbi, group);
- atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
- f)->used_dirs);
- }
- }
-
- if (ext4_has_group_desc_csum(sb)) {
- bool fast_crc = true;
- int free = EXT4_INODES_PER_GROUP(sb) -
- ext4_itable_unused_count(sb, gdp);
-
- if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
- gdp->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT);
- free = 0;
- /* Incremental CRC needs a valid csum baseline */
- fast_crc = false;
- }
- /*
- * Check the relative inode number against the
- * last used relative inode number in this group.
- * If it is greater we need to update the
- * bg_itable_unused count.
- */
- if (bit >= free)
- ext4_itable_unused_set(sb, gdp,
- EXT4_INODES_PER_GROUP(sb) - bit - 1);
- if (fast_crc)
- ext4_inode_bitmap_csum_set_fast(sb, gdp, bit);
- else
- ext4_inode_bitmap_csum_set(sb, gdp, inode_bitmap_bh);
- ext4_group_desc_csum_set(sb, group, gdp);
- }
-unlock_group:
+ if (!ret2)
+ ext4_update_inode_group_desc(sb, group, gdp,
+ inode_bitmap_bh, bit, mode);
ext4_unlock_group(sb, group);
if (ext4_has_group_desc_csum(sb) &&
!(sbi->s_mount_state & EXT4_FC_REPLAY))
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 09/17] ext4: add ext4_inode_bitmap_csum_set_fast() for incremental checksum update
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Add a helper function ext4_inode_bitmap_csum_set_fast() that uses
crc32c_flip_range() to incrementally update the inode bitmap checksum
after flipping a single bit at the given offset. This avoids a full
bitmap CRC rescan, computing the CRC delta in O(log N) time.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/bitmap.c | 23 +++++++++++++++++++++++
fs/ext4/ext4.h | 3 +++
2 files changed, 26 insertions(+)
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
index 008acc439301..ea47ca0d7046 100644
--- a/fs/ext4/bitmap.c
+++ b/fs/ext4/bitmap.c
@@ -71,6 +71,29 @@ void ext4_inode_bitmap_csum_set(struct super_block *sb,
ext4_inode_bitmap_csum_store(sb, gdp, csum);
}
+/*
+ * Update inode bitmap checksum for a single flipped bit.
+ *
+ * Use crc32c_flip_range() to incrementally update the checksum after
+ * flipping the bit at @offset, avoiding a full bitmap CRC rescan.
+ * The csum_seed cancels out in the XOR delta, so it is not needed here.
+ */
+void ext4_inode_bitmap_csum_set_fast(struct super_block *sb,
+ struct ext4_group_desc *gdp,
+ ext4_grpblk_t offset)
+{
+ __u32 new_csum, old_csum;
+
+ if (!ext4_has_feature_metadata_csum(sb))
+ return;
+
+ old_csum = ext4_inode_bitmap_csum_get(sb, gdp);
+ new_csum = crc32c_flip_range(old_csum, EXT4_INODES_PER_GROUP(sb),
+ offset, 1);
+
+ ext4_inode_bitmap_csum_store(sb, gdp, new_csum);
+}
+
static inline __u32 ext4_block_bitmap_csum_get(struct super_block *sb,
struct ext4_group_desc *gdp)
{
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index c423a9a04047..e6739d5af490 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2764,6 +2764,9 @@ extern unsigned int ext4_count_free(char *bitmap, unsigned numchars);
void ext4_inode_bitmap_csum_set(struct super_block *sb,
struct ext4_group_desc *gdp,
struct buffer_head *bh);
+void ext4_inode_bitmap_csum_set_fast(struct super_block *sb,
+ struct ext4_group_desc *gdp,
+ ext4_grpblk_t offset);
int ext4_inode_bitmap_csum_verify(struct super_block *sb,
struct ext4_group_desc *gdp,
struct buffer_head *bh);
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 17/17] ext4: add ext4_get_flex_group() helper to simplify flex group lookups
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Introduce ext4_get_flex_group() that combines ext4_flex_group() and
sbi_array_rcu_deref() into a single call, replacing the repeated
pattern across ialloc.c, mballoc.c, resize.c, and super.c.
No functional change.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/ext4.h | 7 +++++++
fs/ext4/ialloc.c | 19 +++++--------------
fs/ext4/mballoc.c | 4 +---
fs/ext4/resize.c | 4 +---
fs/ext4/super.c | 4 +---
5 files changed, 15 insertions(+), 23 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f48cb9d998ab..e38ada51972a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3457,6 +3457,13 @@ static inline unsigned int ext4_flex_bg_size(struct ext4_sb_info *sbi)
return 1 << sbi->s_log_groups_per_flex;
}
+static inline struct flex_groups *ext4_get_flex_group(struct ext4_sb_info *sbi,
+ ext4_group_t block_group)
+{
+ return sbi_array_rcu_deref(sbi, s_flex_groups,
+ ext4_flex_group(sbi, block_group));
+}
+
static inline loff_t ext4_get_maxbytes(struct inode *inode)
{
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 25430c572818..d88160afd6b6 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -336,8 +336,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
if (sbi->s_log_groups_per_flex) {
struct flex_groups *fg;
- fg = sbi_array_rcu_deref(sbi, s_flex_groups,
- ext4_flex_group(sbi, block_group));
+ fg = ext4_get_flex_group(sbi, block_group);
atomic_inc(&fg->free_inodes);
if (is_directory)
atomic_dec(&fg->used_dirs);
@@ -826,12 +825,8 @@ static void ext4_update_inode_group_desc(struct super_block *sb,
ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
if (S_ISDIR(mode)) {
ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
- if (sbi->s_log_groups_per_flex) {
- ext4_group_t f = ext4_flex_group(sbi, group);
-
- atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
- f)->used_dirs);
- }
+ if (sbi->s_log_groups_per_flex)
+ atomic_inc(&ext4_get_flex_group(sbi, group)->used_dirs);
}
if (!ext4_has_group_desc_csum(sb))
@@ -997,7 +992,6 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
int ret2, err;
struct inode *ret;
ext4_group_t i;
- ext4_group_t flex_group;
struct ext4_group_info *grp = NULL;
bool encrypt = false;
@@ -1220,11 +1214,8 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
if (S_ISDIR(mode))
percpu_counter_inc(&sbi->s_dirs_counter);
- if (sbi->s_log_groups_per_flex) {
- flex_group = ext4_flex_group(sbi, group);
- atomic_dec(&sbi_array_rcu_deref(sbi, s_flex_groups,
- flex_group)->free_inodes);
- }
+ if (sbi->s_log_groups_per_flex)
+ atomic_dec(&ext4_get_flex_group(sbi, group)->free_inodes);
/* the inode bitmap is zero-based */
inode->i_ino = bit + 1 + group * EXT4_INODES_PER_GROUP(sb);
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 77f6309916d1..9e30c9eefd35 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -4181,9 +4181,7 @@ ext4_mb_mark_context(handle_t *handle, struct super_block *sb, bool state,
*ret_changed = changed;
if (sbi->s_log_groups_per_flex) {
- ext4_group_t flex_group = ext4_flex_group(sbi, group);
- struct flex_groups *fg = sbi_array_rcu_deref(sbi,
- s_flex_groups, flex_group);
+ struct flex_groups *fg = ext4_get_flex_group(sbi, group);
if (state)
atomic64_sub(changed, &fg->free_clusters);
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index 2c5b851c552a..8d2cd1bc17bb 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -1495,11 +1495,9 @@ static void ext4_update_super(struct super_block *sb,
ext4_debug("free blocks count %llu",
percpu_counter_read(&sbi->s_freeclusters_counter));
if (ext4_has_feature_flex_bg(sb) && sbi->s_log_groups_per_flex) {
- ext4_group_t flex_group;
struct flex_groups *fg;
- flex_group = ext4_flex_group(sbi, group_data[0].group);
- fg = sbi_array_rcu_deref(sbi, s_flex_groups, flex_group);
+ fg = ext4_get_flex_group(sbi, group_data[0].group);
atomic64_add(EXT4_NUM_B2C(sbi, free_blocks),
&fg->free_clusters);
atomic_add(EXT4_INODES_PER_GROUP(sb) * flex_gd->count,
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 6a77db4d3124..064e06163716 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3211,7 +3211,6 @@ static int ext4_fill_flex_info(struct super_block *sb)
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_group_desc *gdp = NULL;
struct flex_groups *fg;
- ext4_group_t flex_group;
int i, err;
sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
@@ -3227,8 +3226,7 @@ static int ext4_fill_flex_info(struct super_block *sb)
for (i = 0; i < sbi->s_groups_count; i++) {
gdp = ext4_get_group_desc(sb, i, NULL);
- flex_group = ext4_flex_group(sbi, i);
- fg = sbi_array_rcu_deref(sbi, s_flex_groups, flex_group);
+ fg = ext4_get_flex_group(sbi, i);
atomic_add(ext4_free_inodes_count(sb, gdp), &fg->free_inodes);
atomic64_add(ext4_free_group_clusters(sb, gdp),
&fg->free_clusters);
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 10/17] ext4: use fast incremental CRC update in ext4_free_inode()
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Replace ext4_inode_bitmap_csum_set() with the newly added
ext4_inode_bitmap_csum_set_fast() in ext4_free_inode() for incremental
inode bitmap checksum update.
This is safe because:
- At inode free time, the inode bitmap checksum has already been
initialized, so the old checksum is always valid.
- The bitmap buffer modification and checksum update are protected
by the same group lock, ensuring the old checksum is consistent
with the bitmap content before the bit flip.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/ialloc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 3fd8f0099852..55eb69fbb4c9 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -327,7 +327,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
if (percpu_counter_initialized(&sbi->s_dirs_counter))
percpu_counter_dec(&sbi->s_dirs_counter);
}
- ext4_inode_bitmap_csum_set(sb, gdp, bitmap_bh);
+ ext4_inode_bitmap_csum_set_fast(sb, gdp, bit);
ext4_group_desc_csum_set(sb, block_group, gdp);
ext4_unlock_group(sb, block_group);
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 11/17] ext4: fix missing bg_used_dirs_count update in fast commit replay
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
ext4_mark_inode_used() did not update bg_used_dirs_count for directory
inodes during fast commit replay because it lacked the inode mode.
Add a mode parameter and pass it from both ext4_fc_replay_inode() (from
raw_fc_inode) and ext4_fc_replay_create() (after ext4_iget).
Fixes: 8016e29f4362 ("ext4: fast commit recovery path")
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/ext4.h | 2 +-
fs/ext4/fast_commit.c | 13 +++++++------
fs/ext4/ialloc.c | 13 ++++++++++++-
3 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index e6739d5af490..f48cb9d998ab 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2941,7 +2941,7 @@ extern int ext4fs_dirhash(const struct inode *dir, const char *name, int len,
struct dx_hash_info *hinfo);
/* ialloc.c */
-extern int ext4_mark_inode_used(struct super_block *sb, int ino);
+extern int ext4_mark_inode_used(struct super_block *sb, int ino, umode_t mode);
extern struct inode *__ext4_new_inode(struct mnt_idmap *, handle_t *,
struct inode *, umode_t,
const struct qstr *qstr, __u32 goal,
diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
index b3c22636251d..f68d7b2eb0db 100644
--- a/fs/ext4/fast_commit.c
+++ b/fs/ext4/fast_commit.c
@@ -1578,7 +1578,7 @@ static int ext4_fc_replay_inode(struct super_block *sb,
ret = sync_dirty_buffer(iloc.bh);
if (ret)
goto out_brelse;
- ret = ext4_mark_inode_used(sb, ino);
+ ret = ext4_mark_inode_used(sb, ino, le16_to_cpu(raw_fc_inode->i_mode));
if (ret)
goto out_brelse;
@@ -1635,11 +1635,7 @@ static int ext4_fc_replay_create(struct super_block *sb,
trace_ext4_fc_replay(sb, EXT4_FC_TAG_CREAT, darg.ino,
darg.parent_ino, darg.dname_len);
- /* This takes care of update group descriptor and other metadata */
- ret = ext4_mark_inode_used(sb, darg.ino);
- if (ret)
- goto out;
-
+ /* Inode already on disk from TAG_INODE replay; iget first for mode. */
inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
if (IS_ERR(inode)) {
ext4_debug("inode %d not found.", darg.ino);
@@ -1648,6 +1644,11 @@ static int ext4_fc_replay_create(struct super_block *sb,
goto out;
}
+ /* This takes care of update group descriptor and other metadata */
+ ret = ext4_mark_inode_used(sb, darg.ino, inode->i_mode);
+ if (ret)
+ goto out;
+
if (S_ISDIR(inode->i_mode)) {
/*
* If we are creating a directory, we need to make sure that the
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 55eb69fbb4c9..5896cdfb2ccf 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -756,11 +756,12 @@ static int find_inode_bit(struct super_block *sb, ext4_group_t group,
return 1;
}
-int ext4_mark_inode_used(struct super_block *sb, int ino)
+int ext4_mark_inode_used(struct super_block *sb, int ino, umode_t mode)
{
unsigned long max_ino = le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count);
struct buffer_head *inode_bitmap_bh = NULL, *group_desc_bh = NULL;
struct ext4_group_desc *gdp;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_group_t group;
int bit;
int err;
@@ -858,6 +859,16 @@ int ext4_mark_inode_used(struct super_block *sb, int ino)
}
ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
+ if (S_ISDIR(mode)) {
+ ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
+ if (sbi->s_log_groups_per_flex) {
+ ext4_group_t f = ext4_flex_group(sbi, group);
+
+ atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
+ f)->used_dirs);
+ }
+ }
+
if (ext4_has_group_desc_csum(sb)) {
ext4_inode_bitmap_csum_set(sb, gdp, inode_bitmap_bh);
ext4_group_desc_csum_set(sb, group, gdp);
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 08/17] ext4: extract inode bitmap checksum get and store helpers
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Add ext4_inode_bitmap_csum_get() and ext4_inode_bitmap_csum_store()
helpers, and use EXT4_DESC_SIZE(sb) instead of sbi->s_desc_size for
consistency. No functional change.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/bitmap.c | 31 ++++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
index 00b0a3c74859..008acc439301 100644
--- a/fs/ext4/bitmap.c
+++ b/fs/ext4/bitmap.c
@@ -16,11 +16,29 @@ unsigned int ext4_count_free(char *bitmap, unsigned int numchars)
return numchars * BITS_PER_BYTE - memweight(bitmap, numchars);
}
+static inline __u32 ext4_inode_bitmap_csum_get(struct super_block *sb,
+ struct ext4_group_desc *gdp)
+{
+ __u32 csum = le16_to_cpu(gdp->bg_inode_bitmap_csum_lo);
+
+ if (EXT4_DESC_SIZE(sb) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ csum |= (__u32)le16_to_cpu(gdp->bg_inode_bitmap_csum_hi) << 16;
+ return csum;
+}
+
+static inline void ext4_inode_bitmap_csum_store(struct super_block *sb,
+ struct ext4_group_desc *gdp,
+ __u32 csum)
+{
+ gdp->bg_inode_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF);
+ if (EXT4_DESC_SIZE(sb) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ gdp->bg_inode_bitmap_csum_hi = cpu_to_le16(csum >> 16);
+}
+
int ext4_inode_bitmap_csum_verify(struct super_block *sb,
struct ext4_group_desc *gdp,
struct buffer_head *bh)
{
- __u32 hi;
__u32 provided, calculated;
struct ext4_sb_info *sbi = EXT4_SB(sb);
int sz;
@@ -29,12 +47,9 @@ int ext4_inode_bitmap_csum_verify(struct super_block *sb,
return 1;
sz = EXT4_INODES_PER_GROUP(sb) >> 3;
- provided = le16_to_cpu(gdp->bg_inode_bitmap_csum_lo);
+ provided = ext4_inode_bitmap_csum_get(sb, gdp);
calculated = ext4_chksum(sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
- if (sbi->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) {
- hi = le16_to_cpu(gdp->bg_inode_bitmap_csum_hi);
- provided |= (hi << 16);
- } else
+ if (EXT4_DESC_SIZE(sb) < EXT4_BG_INODE_BITMAP_CSUM_HI_END)
calculated &= 0xFFFF;
return provided == calculated;
@@ -53,9 +68,7 @@ void ext4_inode_bitmap_csum_set(struct super_block *sb,
sz = EXT4_INODES_PER_GROUP(sb) >> 3;
csum = ext4_chksum(sbi->s_csum_seed, (__u8 *)bh->b_data, sz);
- gdp->bg_inode_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF);
- if (sbi->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
- gdp->bg_inode_bitmap_csum_hi = cpu_to_le16(csum >> 16);
+ ext4_inode_bitmap_csum_store(sb, gdp, csum);
}
static inline __u32 ext4_block_bitmap_csum_get(struct super_block *sb,
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 13/17] ext4: use fast incremental CRC update in ext4_mark_inode_used()
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Move the bitmap modification, GDP update, and checksum update into a
single group lock acquisition in ext4_mark_inode_used(), eliminating the
race window where another thread could interleave a full recomputation
between bitmap modification and checksum update.
Add a fast_crc flag to select between incremental and full CRC update.
When EXT4_BG_INODE_UNINIT is set, the stored checksum in the group
descriptor is not a valid CRC of the bitmap -- mkfs leaves it as zero
for UNINIT groups, and ext4_read_inode_bitmap() memsets the buffer to
zero without updating the gdp checksum. So fast_crc is forced to false
to fall back to ext4_inode_bitmap_csum_set() for a full recalculation
that establishes a correct baseline.
For non-UNINIT groups, ext4_inode_bitmap_csum_set_fast() computes the
CRC delta for the single flipped bit in O(log N) time.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/ialloc.c | 69 ++++++++++++++++++++++++------------------------
1 file changed, 35 insertions(+), 34 deletions(-)
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 90896b7f8c73..e209e27f827f 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -838,35 +838,37 @@ int ext4_mark_inode_used(struct super_block *sb, int ino, umode_t mode)
goto out;
}
- ext4_set_bit(bit, inode_bitmap_bh->b_data);
-
- BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata");
- err = ext4_handle_dirty_metadata(NULL, NULL, inode_bitmap_bh);
- if (err) {
- ext4_std_error(sb, err);
- goto out;
- }
- err = sync_dirty_buffer(inode_bitmap_bh);
- if (err) {
- ext4_std_error(sb, err);
- goto out;
- }
-
/* We may have to initialize the block bitmap if it isn't already */
err = ext4_might_init_block_bitmap(NULL, sb, group, gdp);
if (err)
goto out;
+ ext4_lock_group(sb, group);
+ /* Fast commit replay is single-threaded, no need for test_and_set */
+ ext4_set_bit(bit, inode_bitmap_bh->b_data);
+
/* Update the relevant bg descriptor fields */
+ ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
+ if (S_ISDIR(mode)) {
+ ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
+ if (sbi->s_log_groups_per_flex) {
+ ext4_group_t f = ext4_flex_group(sbi, group);
+
+ atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
+ f)->used_dirs);
+ }
+ }
+
if (ext4_has_group_desc_csum(sb)) {
- int free;
+ bool fast_crc = true;
+ int free = EXT4_INODES_PER_GROUP(sb) -
+ ext4_itable_unused_count(sb, gdp);
- ext4_lock_group(sb, group); /* while we modify the bg desc */
- free = EXT4_INODES_PER_GROUP(sb) -
- ext4_itable_unused_count(sb, gdp);
if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
gdp->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT);
free = 0;
+ /* Incremental CRC needs a valid checksum baseline */
+ fast_crc = false;
}
/*
@@ -877,27 +879,26 @@ int ext4_mark_inode_used(struct super_block *sb, int ino, umode_t mode)
if (bit >= free)
ext4_itable_unused_set(sb, gdp,
(EXT4_INODES_PER_GROUP(sb) - bit - 1));
- } else {
- ext4_lock_group(sb, group);
+ if (fast_crc)
+ ext4_inode_bitmap_csum_set_fast(sb, gdp, bit);
+ else
+ ext4_inode_bitmap_csum_set(sb, gdp, inode_bitmap_bh);
+ ext4_group_desc_csum_set(sb, group, gdp);
}
- ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
- if (S_ISDIR(mode)) {
- ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
- if (sbi->s_log_groups_per_flex) {
- ext4_group_t f = ext4_flex_group(sbi, group);
+ ext4_unlock_group(sb, group);
- atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
- f)->used_dirs);
- }
+ BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata");
+ err = ext4_handle_dirty_metadata(NULL, NULL, inode_bitmap_bh);
+ if (err) {
+ ext4_std_error(sb, err);
+ goto out;
}
-
- if (ext4_has_group_desc_csum(sb)) {
- ext4_inode_bitmap_csum_set(sb, gdp, inode_bitmap_bh);
- ext4_group_desc_csum_set(sb, group, gdp);
+ err = sync_dirty_buffer(inode_bitmap_bh);
+ if (err) {
+ ext4_std_error(sb, err);
+ goto out;
}
-
- ext4_unlock_group(sb, group);
err = ext4_handle_dirty_metadata(NULL, NULL, group_desc_bh);
sync_dirty_buffer(group_desc_bh);
out:
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 12/17] ext4: factor out ext4_might_init_block_bitmap() helper
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Extract the BLOCK_UNINIT initialization logic from ext4_mark_inode_used()
and __ext4_new_inode() into a shared ext4_might_init_block_bitmap() helper.
Both call sites perform the same sequence: check EXT4_BG_BLOCK_UNINIT,
read the block bitmap, dirty it, then clear the flag and establish the
correct block bitmap checksum under the group lock. The only difference
is whether a journal handle is available (NULL during fast commit replay
in ext4_mark_inode_used()).
No functional change.
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/ialloc.c | 129 +++++++++++++++++++++--------------------------
1 file changed, 58 insertions(+), 71 deletions(-)
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 5896cdfb2ccf..90896b7f8c73 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -756,6 +756,58 @@ static int find_inode_bit(struct super_block *sb, ext4_group_t group,
return 1;
}
+/*
+ * If the block bitmap for @group is not yet initialized (EXT4_BG_BLOCK_UNINIT),
+ * read it into memory, dirty it, and clear the UNINIT flag under the group lock
+ * so that the on-disk checksum is established. @handle may be NULL during fast
+ * commit replay (no journal credits needed in that path).
+ */
+static int ext4_might_init_block_bitmap(handle_t *handle,
+ struct super_block *sb,
+ ext4_group_t group,
+ struct ext4_group_desc *gdp)
+{
+ int err;
+ struct buffer_head *block_bitmap_bh;
+
+ if (!ext4_has_group_desc_csum(sb) ||
+ !(gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)))
+ return 0;
+
+ block_bitmap_bh = ext4_read_block_bitmap(sb, group);
+ if (IS_ERR(block_bitmap_bh))
+ return PTR_ERR(block_bitmap_bh);
+
+ if (handle) {
+ BUFFER_TRACE(block_bitmap_bh, "get block bitmap access");
+ err = ext4_journal_get_write_access(handle, sb,
+ block_bitmap_bh, EXT4_JTR_NONE);
+ if (err)
+ goto out_brelse;
+ }
+
+ BUFFER_TRACE(block_bitmap_bh, "dirty block bitmap");
+ err = ext4_handle_dirty_metadata(handle, NULL, block_bitmap_bh);
+ if (!handle)
+ sync_dirty_buffer(block_bitmap_bh);
+
+ /* recheck and clear flag under lock if we still need to */
+ ext4_lock_group(sb, group);
+ if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
+ gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
+ ext4_free_group_clusters_set(sb, gdp,
+ ext4_free_clusters_after_init(sb, group, gdp));
+ ext4_block_bitmap_csum_set(sb, gdp, block_bitmap_bh);
+ ext4_group_desc_csum_set(sb, group, gdp);
+ }
+ ext4_unlock_group(sb, group);
+
+out_brelse:
+ brelse(block_bitmap_bh);
+ ext4_std_error(sb, err);
+ return err;
+}
+
int ext4_mark_inode_used(struct super_block *sb, int ino, umode_t mode)
{
unsigned long max_ino = le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count);
@@ -801,38 +853,9 @@ int ext4_mark_inode_used(struct super_block *sb, int ino, umode_t mode)
}
/* We may have to initialize the block bitmap if it isn't already */
- if (ext4_has_group_desc_csum(sb) &&
- gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
- struct buffer_head *block_bitmap_bh;
-
- block_bitmap_bh = ext4_read_block_bitmap(sb, group);
- if (IS_ERR(block_bitmap_bh)) {
- err = PTR_ERR(block_bitmap_bh);
- goto out;
- }
-
- BUFFER_TRACE(block_bitmap_bh, "dirty block bitmap");
- err = ext4_handle_dirty_metadata(NULL, NULL, block_bitmap_bh);
- sync_dirty_buffer(block_bitmap_bh);
-
- /* recheck and clear flag under lock if we still need to */
- ext4_lock_group(sb, group);
- if (ext4_has_group_desc_csum(sb) &&
- (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
- gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
- ext4_free_group_clusters_set(sb, gdp,
- ext4_free_clusters_after_init(sb, group, gdp));
- ext4_block_bitmap_csum_set(sb, gdp, block_bitmap_bh);
- ext4_group_desc_csum_set(sb, group, gdp);
- }
- ext4_unlock_group(sb, group);
- brelse(block_bitmap_bh);
-
- if (err) {
- ext4_std_error(sb, err);
- goto out;
- }
- }
+ err = ext4_might_init_block_bitmap(NULL, sb, group, gdp);
+ if (err)
+ goto out;
/* Update the relevant bg descriptor fields */
if (ext4_has_group_desc_csum(sb)) {
@@ -1154,45 +1177,9 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
}
/* We may have to initialize the block bitmap if it isn't already */
- if (ext4_has_group_desc_csum(sb) &&
- gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
- struct buffer_head *block_bitmap_bh;
-
- block_bitmap_bh = ext4_read_block_bitmap(sb, group);
- if (IS_ERR(block_bitmap_bh)) {
- err = PTR_ERR(block_bitmap_bh);
- goto out;
- }
- BUFFER_TRACE(block_bitmap_bh, "get block bitmap access");
- err = ext4_journal_get_write_access(handle, sb, block_bitmap_bh,
- EXT4_JTR_NONE);
- if (err) {
- brelse(block_bitmap_bh);
- ext4_std_error(sb, err);
- goto out;
- }
-
- BUFFER_TRACE(block_bitmap_bh, "dirty block bitmap");
- err = ext4_handle_dirty_metadata(handle, NULL, block_bitmap_bh);
-
- /* recheck and clear flag under lock if we still need to */
- ext4_lock_group(sb, group);
- if (ext4_has_group_desc_csum(sb) &&
- (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
- gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
- ext4_free_group_clusters_set(sb, gdp,
- ext4_free_clusters_after_init(sb, group, gdp));
- ext4_block_bitmap_csum_set(sb, gdp, block_bitmap_bh);
- ext4_group_desc_csum_set(sb, group, gdp);
- }
- ext4_unlock_group(sb, group);
- brelse(block_bitmap_bh);
-
- if (err) {
- ext4_std_error(sb, err);
- goto out;
- }
- }
+ err = ext4_might_init_block_bitmap(handle, sb, group, gdp);
+ if (err)
+ goto out;
/* Update the relevant bg descriptor fields */
if (ext4_has_group_desc_csum(sb)) {
--
2.43.7
^ permalink raw reply related
* [PATCH RFC 15/17] ext4: use fast incremental CRC update in __ext4_new_inode()
From: Baokun Li @ 2026-05-08 12:15 UTC (permalink / raw)
To: linux-ext4
Cc: linux-crypto, ebiggers, ardb, tytso, adilger.kernel, jack,
yi.zhang, ojaswin, ritesh.list, Baokun Li
In-Reply-To: <20260508121539.4174601-1-libaokun@linux.alibaba.com>
Merge the bitmap modification and group descriptor update into a single
group lock acquisition in __ext4_new_inode(). Previously the bitmap bit
was set under one lock/unlock pair, and the GDP fields (UNINIT,
itable_unused, free_inodes, dirs, csum) were updated under a separate
lock/unlock pair with a gap in between. Another thread could modify the
bitmap and update the checksum during that gap, making incremental CRC
incorrect.
Now the full sequence -- set bit, update free inodes, clear UNINIT,
update itable_unused, and compute checksum -- happens atomically under
the same ext4_lock_group(). The alloc_sem is acquired before the group
lock to maintain correct locking order with itable lazyinit.
Use ext4_inode_bitmap_csum_set_fast() for the normal path where the
stored checksum is valid. When EXT4_BG_INODE_UNINIT is set, fall back
to ext4_inode_bitmap_csum_set() for a full recalculation to establish
a correct baseline (mkfs leaves the checksum as zero for UNINIT groups).
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
---
fs/ext4/ialloc.c | 129 +++++++++++++++++++++++------------------------
1 file changed, 63 insertions(+), 66 deletions(-)
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 8b75b331b26e..9dd1cdb367ba 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -1135,7 +1135,25 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
ext4_std_error(sb, err);
goto out;
}
+
+ BUFFER_TRACE(group_desc_bh, "get_write_access");
+ err = ext4_journal_get_write_access(handle, sb, group_desc_bh,
+ EXT4_JTR_NONE);
+ if (err) {
+ ext4_std_error(sb, err);
+ goto out;
+ }
+
+ /* We may have to initialize the block bitmap if it isn't already */
+ err = ext4_might_init_block_bitmap(handle, sb, group, gdp);
+ if (err)
+ goto out;
+
+ if (ext4_has_group_desc_csum(sb) &&
+ !(sbi->s_mount_state & EXT4_FC_REPLAY))
+ down_read(&grp->alloc_sem);
ext4_lock_group(sb, group);
+
ret2 = ext4_test_and_set_bit(bit, inode_bitmap_bh->b_data);
if (ret2) {
/* Someone already took the bit. Repeat the search
@@ -1147,9 +1165,54 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
ret2 = 0;
} else {
ret2 = 1; /* we didn't grab the inode */
+ goto unlock_group;
+ }
+ }
+
+ /* Update the relevant bg descriptor fields */
+ ext4_free_inodes_set(sb, gdp,
+ ext4_free_inodes_count(sb, gdp) - 1);
+ if (S_ISDIR(mode)) {
+ ext4_used_dirs_set(sb, gdp,
+ ext4_used_dirs_count(sb, gdp) + 1);
+ if (sbi->s_log_groups_per_flex) {
+ ext4_group_t f = ext4_flex_group(sbi, group);
+ atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
+ f)->used_dirs);
+ }
+ }
+
+ if (ext4_has_group_desc_csum(sb)) {
+ bool fast_crc = true;
+ int free = EXT4_INODES_PER_GROUP(sb) -
+ ext4_itable_unused_count(sb, gdp);
+
+ if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
+ gdp->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT);
+ free = 0;
+ /* Incremental CRC needs a valid csum baseline */
+ fast_crc = false;
}
+ /*
+ * Check the relative inode number against the
+ * last used relative inode number in this group.
+ * If it is greater we need to update the
+ * bg_itable_unused count.
+ */
+ if (bit >= free)
+ ext4_itable_unused_set(sb, gdp,
+ EXT4_INODES_PER_GROUP(sb) - bit - 1);
+ if (fast_crc)
+ ext4_inode_bitmap_csum_set_fast(sb, gdp, bit);
+ else
+ ext4_inode_bitmap_csum_set(sb, gdp, inode_bitmap_bh);
+ ext4_group_desc_csum_set(sb, group, gdp);
}
+unlock_group:
ext4_unlock_group(sb, group);
+ if (ext4_has_group_desc_csum(sb) &&
+ !(sbi->s_mount_state & EXT4_FC_REPLAY))
+ up_read(&grp->alloc_sem);
if (!ret2)
goto got; /* we grabbed the inode! */
@@ -1168,72 +1231,6 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
goto out;
}
- BUFFER_TRACE(group_desc_bh, "get_write_access");
- err = ext4_journal_get_write_access(handle, sb, group_desc_bh,
- EXT4_JTR_NONE);
- if (err) {
- ext4_std_error(sb, err);
- goto out;
- }
-
- /* We may have to initialize the block bitmap if it isn't already */
- err = ext4_might_init_block_bitmap(handle, sb, group, gdp);
- if (err)
- goto out;
-
- /* Update the relevant bg descriptor fields */
- if (ext4_has_group_desc_csum(sb)) {
- int free;
- struct ext4_group_info *grp = NULL;
-
- if (!(sbi->s_mount_state & EXT4_FC_REPLAY)) {
- grp = ext4_get_group_info(sb, group);
- if (!grp) {
- err = -EFSCORRUPTED;
- goto out;
- }
- down_read(&grp->alloc_sem); /*
- * protect vs itable
- * lazyinit
- */
- }
- ext4_lock_group(sb, group); /* while we modify the bg desc */
- free = EXT4_INODES_PER_GROUP(sb) -
- ext4_itable_unused_count(sb, gdp);
- if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
- gdp->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT);
- free = 0;
- }
- /*
- * Check the relative inode number against the last used
- * relative inode number in this group. if it is greater
- * we need to update the bg_itable_unused count
- */
- if (bit >= free)
- ext4_itable_unused_set(sb, gdp,
- (EXT4_INODES_PER_GROUP(sb) - bit - 1));
- if (!(sbi->s_mount_state & EXT4_FC_REPLAY))
- up_read(&grp->alloc_sem);
- } else {
- ext4_lock_group(sb, group);
- }
-
- ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);
- if (S_ISDIR(mode)) {
- ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1);
- if (sbi->s_log_groups_per_flex) {
- ext4_group_t f = ext4_flex_group(sbi, group);
-
- atomic_inc(&sbi_array_rcu_deref(sbi, s_flex_groups,
- f)->used_dirs);
- }
- }
- if (ext4_has_group_desc_csum(sb)) {
- ext4_inode_bitmap_csum_set(sb, gdp, inode_bitmap_bh);
- ext4_group_desc_csum_set(sb, group, gdp);
- }
- ext4_unlock_group(sb, group);
-
BUFFER_TRACE(group_desc_bh, "call ext4_handle_dirty_metadata");
err = ext4_handle_dirty_metadata(handle, NULL, group_desc_bh);
if (err) {
--
2.43.7
^ permalink raw reply related
* Re: [PATCH] crypto : ecc - Fix carry overflow in vli multiplication
From: Stefan Berger @ 2026-05-08 14:36 UTC (permalink / raw)
To: Anastasia Tishchenko, Lukas Wunner, Ignat Korchagin, Herbert Xu,
David S . Miller
Cc: linux-crypto, linux-kernel
In-Reply-To: <20260508114844.29694-1-sv3iry@gmail.com>
On 5/8/26 7:48 AM, Anastasia Tishchenko wrote:
> The carry flag calculation fails when r01.m_high is saturated
> (0xFFFFFFFFFFFFFFFF) and addition of lower bits overflows.
>
> The condition (r01.m_high < product.m_high) doesn't handle the case
> where r01.m_high == product.m_high and an additional carry exists
> from lower-bit overflow.
>
> Add proper handling for this boundary by accounting for the carry
> from the lower addition.
>
> This issue was discovered during formal verification of ECC functions.
Thanks!
>
> Signed-off-by: Anastasia Tishchenko <sv3iry@gmail.com>
> ---
> crypto/ecc.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/crypto/ecc.c b/crypto/ecc.c
> index 43b0def3a225..dfe96471407c 100644
> --- a/crypto/ecc.c
> +++ b/crypto/ecc.c
> @@ -427,7 +427,10 @@ static void vli_mult(u64 *result, const u64 *left, const u64 *right,
> product = mul_64_64(left[i], right[k - i]);
>
> r01 = add_128_128(r01, product);
> - r2 += (r01.m_high < product.m_high);
Maybe the following or something like lt_128(r01, product) would be
'better' replacing 'r01 < product':
if (cmp_128(r01, product) < 0)
r2++;
/* Compare two uint128_t; returns -1 if a<b, 0 if a == b, 1 otherwise */
static int cmp_128(uint128_t a, uint128_t b)
{
if (a.m_high < b.m_high)
return -1;
if (a.m_high > b.m_high)
return 1;
if (a.m_low < b.m_low)
return -1;
if (a.m_low > b.m_low)
return 1;
return 0;
}
/* r01 < product */
if (lt_128(r01, product))
r2++;
/* Check a < b; return 1 if a < b, 0 otherwise */
static int lt_128(uint128_t a, uint128_t b)
{
if (a.m_high < b.m_high)
return 1;
if (a.m_high > b.m_high)
return 0;
if (a.m_low < b.m_low)
return 1;
return 0;
}
> + if (r01.m_high != product.m_high)
> + r2 += (r01.m_high < product.m_high);
> + else
> + r2 += (r01.m_low < product.m_low);
> }
>
> result[k] = r01.m_low;
> @@ -488,7 +491,10 @@ static void vli_square(u64 *result, const u64 *left, unsigned int ndigits)
> }
>
> r01 = add_128_128(r01, product);
> - r2 += (r01.m_high < product.m_high);
> + if (r01.m_high != product.m_high)
> + r2 += (r01.m_high < product.m_high);
> + else
> + r2 += (r01.m_low < product.m_low);
> }
>
> result[k] = r01.m_low;
^ 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