* [PATCH v22 net-next 08/12] net/nebula-matrix: dispatch: add control-level routing core infrastructure
From: illusion.wang @ 2026-07-23 4:01 UTC (permalink / raw)
To: dimon.zhao, illusion.wang, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, horms, linux-doc, pabeni,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, open list
In-Reply-To: <20260723040110.91410-1-illusion.wang@nebula-matrix.com>
From: illusion wang <illusion.wang@nebula-matrix.com>
Add base dispatch layer infrastructure for control-level routing:
1. Dispatch management & ops table structures allocation
2. X-macro op table template for uniform dispatch entry registration
3. Control PF / regular PF routing logic via ctrl_lvl bitmask
4. Local chip init/deinit dispatch wrappers (no channel dependency)
Document constraint: init_module/deinit_module only valid
on Control PF, caller must guard with has_ctrl to avoid NULL deref.
This patch only provides core routing skeleton, no channel message
handling or resource locking logic.
Signed-off-by: illusion wang <illusion.wang@nebula-matrix.com>
---
.../net/ethernet/nebula-matrix/nbl/Makefile | 1 +
.../nbl/nbl_channel/nbl_channel.c | 4 +
.../net/ethernet/nebula-matrix/nbl/nbl_core.h | 4 +
.../nebula-matrix/nbl/nbl_core/nbl_dispatch.c | 137 ++++++++++++++++++
.../nebula-matrix/nbl/nbl_core/nbl_dispatch.h | 24 +++
.../nbl/nbl_include/nbl_def_channel.h | 26 ++++
.../nbl/nbl_include/nbl_def_dispatch.h | 42 ++++++
.../nbl/nbl_include/nbl_include.h | 20 +++
.../net/ethernet/nebula-matrix/nbl/nbl_main.c | 8 +
9 files changed, 266 insertions(+)
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
index ef5b6ada70e5..56464f576cbe 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
+++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
@@ -10,4 +10,5 @@ nbl-objs += nbl_common/nbl_common.o \
nbl_hw/nbl_resource.o \
nbl_hw/nbl_interrupt.o \
nbl_hw/nbl_chip.o \
+ nbl_core/nbl_dispatch.o \
nbl_main.o
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
index 85218f826f59..eb0d55dad944 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
@@ -1004,6 +1004,10 @@ int nbl_chan_init_common(struct nbl_adapter *adap)
struct nbl_channel_mgt *chan_mgt;
int ret;
+ BUILD_BUG_ON(sizeof(struct nbl_chan_param_cfg_msix_map) != 8);
+ BUILD_BUG_ON(sizeof(struct nbl_chan_param_set_mailbox_irq) != 4);
+ BUILD_BUG_ON(sizeof(struct nbl_chan_param_get_vsi_id) != 4);
+ BUILD_BUG_ON(sizeof(struct nbl_chan_param_get_eth_id) != 8);
chan_mgt = nbl_chan_setup_chan_mgt(adap);
if (IS_ERR(chan_mgt)) {
ret = PTR_ERR(chan_mgt);
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
index 319d105436a1..a1f874bb03c6 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
@@ -14,6 +14,8 @@ struct nbl_hw_mgt;
struct nbl_hw_ops_tbl;
struct nbl_resource_mgt;
struct nbl_resource_ops_tbl;
+struct nbl_dispatch_mgt;
+struct nbl_dispatch_ops_tbl;
struct nbl_channel_ops_tbl;
struct nbl_channel_mgt;
@@ -25,12 +27,14 @@ enum {
struct nbl_interface {
struct nbl_hw_ops_tbl *hw_ops_tbl;
struct nbl_resource_ops_tbl *resource_ops_tbl;
+ struct nbl_dispatch_ops_tbl *dispatch_ops_tbl;
struct nbl_channel_ops_tbl *channel_ops_tbl;
};
struct nbl_core {
struct nbl_hw_mgt *hw_mgt;
struct nbl_resource_mgt *res_mgt;
+ struct nbl_dispatch_mgt *disp_mgt;
struct nbl_channel_mgt *chan_mgt;
};
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c
new file mode 100644
index 000000000000..8116643859c7
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Nebula Matrix Limited.
+ */
+#include <linux/device.h>
+#include <linux/pci.h>
+#include "nbl_dispatch.h"
+
+static void nbl_disp_deinit_module(struct nbl_dispatch_mgt *disp_mgt)
+{
+ struct nbl_resource_ops *res_ops = disp_mgt->res_ops_tbl->ops;
+ struct nbl_resource_mgt *p = disp_mgt->res_ops_tbl->priv;
+
+ NBL_OPS_CALL(res_ops->deinit_module, (p));
+}
+
+static int nbl_disp_init_module(struct nbl_dispatch_mgt *disp_mgt)
+{
+ struct nbl_resource_ops *res_ops = disp_mgt->res_ops_tbl->ops;
+ struct nbl_resource_mgt *p = disp_mgt->res_ops_tbl->priv;
+
+ return NBL_OPS_CALL_RET(res_ops->init_module, (p));
+}
+
+/* NBL_DISP_SET_OPS(disp_op_name, func, ctrl_lvl, msg_type, msg_req, msg_resp)
+ * ctrl_lvl is to define when this disp_op should go directly to res_op,
+ * not sending a channel msg.
+ * Use X Macros to reduce codes in channel_op and disp_op setup/remove
+ *
+ * Note: init_module / deinit_module only valid on Control PF
+ * (has_ctrl=1). On regular PF without MGT ctrl bit, these ops become NULL,
+ * caller must guard with has_ctrl to avoid NULL dereference.
+ */
+#define NBL_DISP_OPS_TBL \
+do { \
+ NBL_DISP_SET_OPS(init_module, nbl_disp_init_module, \
+ NBL_DISP_CTRL_LVL_MGT, -1, NULL, NULL); \
+ NBL_DISP_SET_OPS(deinit_module, \
+ nbl_disp_deinit_module, \
+ NBL_DISP_CTRL_LVL_MGT, -1, NULL, NULL); \
+} while (0)
+
+/* Ctrl lvl means that if a certain level is set, then all disp_ops that
+ * declared this lvl will go directly to res_ops, rather than send a
+ * channel msg, and vice versa.
+ */
+static void nbl_disp_setup_ctrl_lvl(struct nbl_dispatch_mgt *disp_mgt, u32 lvl)
+{
+ struct nbl_dispatch_ops *disp_ops = disp_mgt->disp_ops_tbl->ops;
+
+ set_bit(lvl, disp_mgt->ctrl_lvl);
+
+#define NBL_DISP_SET_OPS(disp_op, func, ctrl, msg_type, msg_req, msg_resp) \
+do { \
+ typeof(msg_type) _msg_type = (msg_type); \
+ (void)(_msg_type); \
+ (void)(msg_resp); \
+ disp_ops->NBL_NAME(disp_op) = \
+ test_bit(ctrl, disp_mgt->ctrl_lvl) ? func : msg_req; \
+} while (0)
+ NBL_DISP_OPS_TBL;
+#undef NBL_DISP_SET_OPS
+}
+
+static struct nbl_dispatch_mgt *
+nbl_disp_setup_disp_mgt(struct nbl_common_info *common)
+{
+ struct nbl_dispatch_mgt *disp_mgt;
+ struct device *dev = common->dev;
+
+ disp_mgt = devm_kzalloc(dev, sizeof(*disp_mgt), GFP_KERNEL);
+ if (!disp_mgt)
+ return ERR_PTR(-ENOMEM);
+
+ disp_mgt->common = common;
+ return disp_mgt;
+}
+
+static struct nbl_dispatch_ops_tbl *
+nbl_disp_setup_ops(struct device *dev, struct nbl_dispatch_mgt *disp_mgt)
+{
+ struct nbl_dispatch_ops_tbl *disp_ops_tbl;
+ struct nbl_dispatch_ops *disp_ops;
+
+ disp_ops_tbl = devm_kzalloc(dev, sizeof(*disp_ops_tbl), GFP_KERNEL);
+ if (!disp_ops_tbl)
+ return ERR_PTR(-ENOMEM);
+
+ disp_ops = devm_kzalloc(dev, sizeof(*disp_ops), GFP_KERNEL);
+ if (!disp_ops)
+ return ERR_PTR(-ENOMEM);
+
+ disp_ops_tbl->ops = disp_ops;
+ disp_ops_tbl->priv = disp_mgt;
+
+ return disp_ops_tbl;
+}
+
+int nbl_disp_init(struct nbl_adapter *adapter)
+{
+ struct nbl_common_info *common = &adapter->common;
+ struct nbl_dispatch_ops_tbl *disp_ops_tbl;
+ struct nbl_resource_ops_tbl *res_ops_tbl =
+ adapter->intf.resource_ops_tbl;
+ struct nbl_channel_ops_tbl *chan_ops_tbl =
+ adapter->intf.channel_ops_tbl;
+ struct device *dev = &adapter->pdev->dev;
+ struct nbl_dispatch_mgt *disp_mgt;
+ int ret;
+
+ disp_mgt = nbl_disp_setup_disp_mgt(common);
+ if (IS_ERR(disp_mgt)) {
+ ret = PTR_ERR(disp_mgt);
+ return ret;
+ }
+
+ disp_ops_tbl = nbl_disp_setup_ops(dev, disp_mgt);
+ if (IS_ERR(disp_ops_tbl)) {
+ ret = PTR_ERR(disp_ops_tbl);
+ return ret;
+ }
+
+ disp_mgt->res_ops_tbl = res_ops_tbl;
+ disp_mgt->chan_ops_tbl = chan_ops_tbl;
+ disp_mgt->disp_ops_tbl = disp_ops_tbl;
+ adapter->core.disp_mgt = disp_mgt;
+ adapter->intf.dispatch_ops_tbl = disp_ops_tbl;
+
+ if (common->has_ctrl)
+ nbl_disp_setup_ctrl_lvl(disp_mgt, NBL_DISP_CTRL_LVL_MGT);
+
+ return 0;
+}
+
+void nbl_disp_remove(struct nbl_adapter *adapter)
+{
+}
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h
new file mode 100644
index 000000000000..f06b90075af4
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025 Nebula Matrix Limited.
+ */
+
+#ifndef _NBL_DISPATCH_H_
+#define _NBL_DISPATCH_H_
+#include "../nbl_include/nbl_include.h"
+#include "../nbl_include/nbl_def_channel.h"
+#include "../nbl_include/nbl_def_hw.h"
+#include "../nbl_include/nbl_def_resource.h"
+#include "../nbl_include/nbl_def_dispatch.h"
+#include "../nbl_include/nbl_def_common.h"
+#include "../nbl_core.h"
+
+struct nbl_dispatch_mgt {
+ struct nbl_common_info *common;
+ struct nbl_resource_ops_tbl *res_ops_tbl;
+ struct nbl_channel_ops_tbl *chan_ops_tbl;
+ struct nbl_dispatch_ops_tbl *disp_ops_tbl;
+ DECLARE_BITMAP(ctrl_lvl, NBL_DISP_CTRL_LVL_MAX);
+};
+
+#endif
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
index e581524888e5..fddceac94b8f 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
@@ -282,6 +282,32 @@ enum nbl_chan_state {
NBL_CHAN_STATE_NBITS
};
+struct nbl_chan_param_cfg_msix_map {
+ __le16 num_net_msix;
+ __le16 num_others_msix;
+ __le16 msix_mask_en;
+ __le16 rsvd;
+};
+
+struct nbl_chan_param_set_mailbox_irq {
+ __le16 vector_id;
+ u8 enable_msix;
+ u8 rsvd;
+};
+
+struct nbl_chan_param_get_vsi_id {
+ __le16 vsi_id;
+ __le16 type;
+};
+
+struct nbl_chan_param_get_eth_id {
+ __le16 vsi_id;
+ u8 eth_num;
+ u8 eth_id;
+ u8 logic_eth_id;
+ u8 rsvd[3];
+};
+
struct nbl_board_port_info {
u8 eth_num;
u8 eth_speed;
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h
new file mode 100644
index 000000000000..4386cfd6faa7
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025 Nebula Matrix Limited.
+ */
+
+#ifndef _NBL_DEF_DISPATCH_H_
+#define _NBL_DEF_DISPATCH_H_
+
+#include <linux/types.h>
+
+struct nbl_dispatch_mgt;
+struct nbl_adapter;
+enum {
+ NBL_DISP_CTRL_LVL_NEVER = 0,
+ NBL_DISP_CTRL_LVL_MGT,
+ NBL_DISP_CTRL_LVL_NET,
+ NBL_DISP_CTRL_LVL_MAX,
+};
+
+struct nbl_dispatch_ops {
+ int (*init_module)(struct nbl_dispatch_mgt *disp_mgt);
+ void (*deinit_module)(struct nbl_dispatch_mgt *disp_mgt);
+ int (*configure_msix_map)(struct nbl_dispatch_mgt *disp_mgt,
+ u16 num_net_msix, u16 num_others_msix,
+ bool net_msix_mask_en);
+ int (*destroy_msix_map)(struct nbl_dispatch_mgt *disp_mgt);
+ int (*set_mailbox_irq)(struct nbl_dispatch_mgt *disp_mgt,
+ u16 vector_id, bool enable_msix);
+ int (*get_vsi_id)(struct nbl_dispatch_mgt *disp_mgt, u16 type,
+ u16 *vsi_id);
+ int (*get_eth_id)(struct nbl_dispatch_mgt *disp_mgt, u16 vsi_id,
+ u8 *eth_num, u8 *eth_id, u8 *logic_eth_id);
+};
+
+struct nbl_dispatch_ops_tbl {
+ struct nbl_dispatch_ops *ops;
+ struct nbl_dispatch_mgt *priv;
+};
+
+int nbl_disp_init(struct nbl_adapter *adapter);
+void nbl_disp_remove(struct nbl_adapter *adapter);
+#endif
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
index 5cd0dac5a776..35fdb4a631c5 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
@@ -19,6 +19,8 @@
#define NBL_MAX_FUNC 520
#define NBL_MAX_ETHERNET 4
+/* Used for macros to pass checkpatch */
+#define NBL_NAME(x) x
enum {
NBL_VSI_DATA = 0,
@@ -35,6 +37,10 @@ struct nbl_init_param {
struct nbl_func_caps caps;
};
+/*
+ * Firmware ABI defines port speed enum fixed, value 0 represents 10G, cannot
+ * reassign 0 to INVALID for compatibility
+ */
enum nbl_fw_port_speed {
NBL_FW_PORT_SPEED_10G,
NBL_FW_PORT_SPEED_25G,
@@ -42,6 +48,20 @@ enum nbl_fw_port_speed {
NBL_FW_PORT_SPEED_100G,
};
+#define NBL_OPS_CALL(func, para) \
+do { \
+ typeof(func) _func = (func); \
+ if (_func) \
+ _func para; \
+} while (0)
+
+/* Optional ops: NULL means not implemented, return 0 = no-op (not an error) */
+#define NBL_OPS_CALL_RET(func, para) \
+({ \
+ typeof(func) _func = (func); \
+ _func ? _func para : 0; \
+})
+
enum nbl_performance_mode {
NBL_QUIRKS_NO_TOE,
NBL_QUIRKS_UVN_PREFETCH_ALIGN,
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
index 0e51dd65ee48..d2ea55f5d568 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
@@ -11,6 +11,7 @@
#include "nbl_include/nbl_def_channel.h"
#include "nbl_include/nbl_def_hw.h"
#include "nbl_include/nbl_def_resource.h"
+#include "nbl_include/nbl_def_dispatch.h"
#include "nbl_include/nbl_def_common.h"
#include "nbl_core.h"
@@ -48,7 +49,13 @@ struct nbl_adapter *nbl_core_init(struct pci_dev *pdev,
ret = nbl_res_init_leonis(adapter);
if (ret)
goto res_init_fail;
+
+ ret = nbl_disp_init(adapter);
+ if (ret)
+ goto disp_init_fail;
return adapter;
+disp_init_fail:
+ nbl_res_remove_leonis(adapter);
res_init_fail:
nbl_chan_remove_common(adapter);
chan_init_fail:
@@ -59,6 +66,7 @@ struct nbl_adapter *nbl_core_init(struct pci_dev *pdev,
void nbl_core_remove(struct nbl_adapter *adapter)
{
+ nbl_disp_remove(adapter);
nbl_res_remove_leonis(adapter);
nbl_chan_remove_common(adapter);
nbl_hw_remove_leonis(adapter);
--
2.47.3
^ permalink raw reply related
* [PATCH v22 net-next 03/12] net/nebula-matrix: add channel wire opcode enum definitions
From: illusion.wang @ 2026-07-23 4:00 UTC (permalink / raw)
To: dimon.zhao, illusion.wang, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, horms, linux-doc, pabeni,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, open list
In-Reply-To: <20260723040110.91410-1-illusion.wang@nebula-matrix.com>
From: illusion wang <illusion.wang@nebula-matrix.com>
Add enum nbl_chan_msg_type into nbl_def_channel.h, defining all
PF/VF/firmware channel wire opcodes used for inter-component mailbox
communication.
Each enumerator carries an explicit fixed numeric value, acting as stable
wire-format message ID shared between driver and firmware.
ABI constraint: New opcodes must only be appended before
NBL_CHAN_MSG_MAILBOX_MAX.
Reordering, inserting or deleting existing entries will break
cross-version driver-firmware interoperability. Any modification to
existing opcode assignments requires coordinated firmware ABI change.
This commit introduces the opcode ID enumeration. Message payload
structures and complete channel layer logic will be added in subsequent
separate patches.
Signed-off-by: illusion wang <illusion.wang@nebula-matrix.com>
---
.../nbl/nbl_include/nbl_def_channel.h | 242 ++++++++++++++++++
1 file changed, 242 insertions(+)
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
new file mode 100644
index 000000000000..be374667c338
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025 Nebula Matrix Limited.
+ */
+
+#ifndef _NBL_DEF_CHANNEL_H_
+#define _NBL_DEF_CHANNEL_H_
+
+/*
+ * Mailbox wire opcodes
+ * Every opcode is assigned explicit fixed numeric value, stable wire ABI
+ * shared between driver and firmware.
+ * Firmware/driver wire compatibility rule:
+ * New entries must only be appended before NBL_CHAN_MSG_MAILBOX_MAX.
+ * Reordering / inserting / deleting existing items will break cross-version
+ * interoperability. Any change to existing enumerators requires matching
+ * firmware ABI adjustment.
+ */
+enum nbl_chan_msg_type {
+ NBL_CHAN_MSG_ACK = 0,
+ NBL_CHAN_MSG_ADD_MACVLAN = 1,
+ NBL_CHAN_MSG_DEL_MACVLAN = 2,
+ NBL_CHAN_MSG_ADD_MULTI_RULE = 3,
+ NBL_CHAN_MSG_DEL_MULTI_RULE = 4,
+ NBL_CHAN_MSG_SETUP_MULTI_GROUP = 5,
+ NBL_CHAN_MSG_REMOVE_MULTI_GROUP = 6,
+ NBL_CHAN_MSG_REGISTER_NET = 7,
+ NBL_CHAN_MSG_UNREGISTER_NET = 8,
+ NBL_CHAN_MSG_ALLOC_TXRX_QUEUES = 9,
+ NBL_CHAN_MSG_FREE_TXRX_QUEUES = 10,
+ NBL_CHAN_MSG_SETUP_QUEUE = 11,
+ NBL_CHAN_MSG_REMOVE_ALL_QUEUES = 12,
+ NBL_CHAN_MSG_CFG_DSCH = 13,
+ NBL_CHAN_MSG_SETUP_CQS = 14,
+ NBL_CHAN_MSG_REMOVE_CQS = 15,
+ NBL_CHAN_MSG_CFG_QDISC_MQPRIO = 16,
+ NBL_CHAN_MSG_CONFIGURE_MSIX_MAP = 17,
+ NBL_CHAN_MSG_DESTROY_MSIX_MAP = 18,
+ NBL_CHAN_MSG_MAILBOX_SET_IRQ = 19,
+ NBL_CHAN_MSG_GET_GLOBAL_VECTOR = 20,
+ NBL_CHAN_MSG_GET_VSI_ID = 21,
+ NBL_CHAN_MSG_SET_PROMISC_MODE = 22,
+ NBL_CHAN_MSG_GET_FIRMWARE_VERSION = 23,
+ NBL_CHAN_MSG_GET_QUEUE_ERR_STATS = 24,
+ NBL_CHAN_MSG_GET_COALESCE = 25,
+ NBL_CHAN_MSG_SET_COALESCE = 26,
+ NBL_CHAN_MSG_SET_SPOOF_CHECK_ADDR = 27,
+ NBL_CHAN_MSG_SET_VF_SPOOF_CHECK = 28,
+ NBL_CHAN_MSG_GET_RXFH_INDIR_SIZE = 29,
+ NBL_CHAN_MSG_GET_RXFH_INDIR = 30,
+ NBL_CHAN_MSG_GET_RXFH_RSS_KEY = 31,
+ NBL_CHAN_MSG_GET_RXFH_RSS_ALG_SEL = 32,
+ NBL_CHAN_MSG_GET_HW_CAPS = 33,
+ NBL_CHAN_MSG_GET_HW_STATE = 34,
+ NBL_CHAN_MSG_REGISTER_RDMA = 35,
+ NBL_CHAN_MSG_UNREGISTER_RDMA = 36,
+ NBL_CHAN_MSG_GET_REAL_HW_ADDR = 37,
+ NBL_CHAN_MSG_GET_REAL_BDF = 38,
+ NBL_CHAN_MSG_GRC_PROCESS = 39,
+ NBL_CHAN_MSG_SET_SFP_STATE = 40,
+ NBL_CHAN_MSG_SET_ETH_LOOPBACK = 41,
+ NBL_CHAN_MSG_CHECK_ACTIVE_VF = 42,
+ NBL_CHAN_MSG_GET_PRODUCT_FLEX_CAP = 43,
+ NBL_CHAN_MSG_ALLOC_KTLS_TX_INDEX = 44,
+ NBL_CHAN_MSG_FREE_KTLS_TX_INDEX = 45,
+ NBL_CHAN_MSG_CFG_KTLS_TX_KEYMAT = 46,
+ NBL_CHAN_MSG_ALLOC_KTLS_RX_INDEX = 47,
+ NBL_CHAN_MSG_FREE_KTLS_RX_INDEX = 48,
+ NBL_CHAN_MSG_CFG_KTLS_RX_KEYMAT = 49,
+ NBL_CHAN_MSG_CFG_KTLS_RX_RECORD = 50,
+ NBL_CHAN_MSG_ADD_KTLS_RX_FLOW = 51,
+ NBL_CHAN_MSG_DEL_KTLS_RX_FLOW = 52,
+ NBL_CHAN_MSG_ALLOC_IPSEC_TX_INDEX = 53,
+ NBL_CHAN_MSG_FREE_IPSEC_TX_INDEX = 54,
+ NBL_CHAN_MSG_ALLOC_IPSEC_RX_INDEX = 55,
+ NBL_CHAN_MSG_FREE_IPSEC_RX_INDEX = 56,
+ NBL_CHAN_MSG_CFG_IPSEC_TX_SAD = 57,
+ NBL_CHAN_MSG_CFG_IPSEC_RX_SAD = 58,
+ NBL_CHAN_MSG_ADD_IPSEC_TX_FLOW = 59,
+ NBL_CHAN_MSG_DEL_IPSEC_TX_FLOW = 60,
+ NBL_CHAN_MSG_ADD_IPSEC_RX_FLOW = 61,
+ NBL_CHAN_MSG_DEL_IPSEC_RX_FLOW = 62,
+ NBL_CHAN_MSG_NOTIFY_IPSEC_HARD_EXPIRE = 63,
+ NBL_CHAN_MSG_GET_MBX_IRQ_NUM = 64,
+ NBL_CHAN_MSG_CLEAR_FLOW = 65,
+ NBL_CHAN_MSG_CLEAR_QUEUE = 66,
+ NBL_CHAN_MSG_GET_ETH_ID = 67,
+ NBL_CHAN_MSG_SET_OFFLOAD_STATUS = 68,
+ NBL_CHAN_MSG_INIT_OFLD = 69,
+ NBL_CHAN_MSG_INIT_CMDQ = 70,
+ NBL_CHAN_MSG_DESTROY_CMDQ = 71,
+ NBL_CHAN_MSG_RESET_CMDQ = 72,
+ NBL_CHAN_MSG_INIT_FLOW = 73,
+ NBL_CHAN_MSG_DEINIT_FLOW = 74,
+ NBL_CHAN_MSG_OFFLOAD_FLOW_RULE = 75,
+ NBL_CHAN_MSG_GET_ACL_SWITCH = 76,
+ NBL_CHAN_MSG_GET_VSI_GLOBAL_QUEUE_ID = 77,
+ NBL_CHAN_MSG_INIT_REP = 78,
+ NBL_CHAN_MSG_GET_LINE_RATE_INFO = 79,
+ NBL_CHAN_MSG_REGISTER_NET_REP = 80,
+ NBL_CHAN_MSG_UNREGISTER_NET_REP = 81,
+ NBL_CHAN_MSG_REGISTER_ETH_REP = 82,
+ NBL_CHAN_MSG_UNREGISTER_ETH_REP = 83,
+ NBL_CHAN_MSG_REGISTER_UPCALL_PORT = 84,
+ NBL_CHAN_MSG_UNREGISTER_UPCALL_PORT = 85,
+ NBL_CHAN_MSG_GET_PORT_STATE = 86,
+ NBL_CHAN_MSG_SET_PORT_ADVERTISING = 87,
+ NBL_CHAN_MSG_GET_MODULE_INFO = 88,
+ NBL_CHAN_MSG_GET_MODULE_EEPROM = 89,
+ NBL_CHAN_MSG_GET_LINK_STATE = 90,
+ NBL_CHAN_MSG_NOTIFY_LINK_STATE = 91,
+ NBL_CHAN_MSG_GET_QUEUE_CXT = 92,
+ NBL_CHAN_MSG_CFG_LOG = 93,
+ NBL_CHAN_MSG_INIT_VDPAQ = 94,
+ NBL_CHAN_MSG_DESTROY_VDPAQ = 95,
+ NBL_CHAN_MSG_GET_UPCALL_PORT = 96,
+ NBL_CHAN_MSG_NOTIFY_ETH_REP_LINK_STATE = 97,
+ NBL_CHAN_MSG_SET_ETH_MAC_ADDR = 98,
+ NBL_CHAN_MSG_GET_FUNCTION_ID = 99,
+ NBL_CHAN_MSG_GET_CHIP_TEMPERATURE = 100,
+ NBL_CHAN_MSG_DISABLE_HW_FLOW = 101,
+ NBL_CHAN_MSG_ENABLE_HW_FLOW = 102,
+ NBL_CHAN_MSG_SET_UPCALL_RULE = 103,
+ NBL_CHAN_MSG_UNSET_UPCALL_RULE = 104,
+ NBL_CHAN_MSG_GET_REG_DUMP = 105,
+ NBL_CHAN_MSG_GET_REG_DUMP_LEN = 106,
+ NBL_CHAN_MSG_CFG_LAG_HASH_ALGORITHM = 107,
+ NBL_CHAN_MSG_CFG_LAG_MEMBER_FWD = 108,
+ NBL_CHAN_MSG_CFG_LAG_MEMBER_LIST = 109,
+ NBL_CHAN_MSG_CFG_LAG_MEMBER_UP_ATTR = 110,
+ NBL_CHAN_MSG_ADD_LAG_FLOW = 111,
+ NBL_CHAN_MSG_DEL_LAG_FLOW = 112,
+ NBL_CHAN_MSG_SWITCHDEV_INIT_CMDQ = 113,
+ NBL_CHAN_MSG_SWITCHDEV_DEINIT_CMDQ = 114,
+ NBL_CHAN_MSG_SET_TC_FLOW_INFO = 115,
+ NBL_CHAN_MSG_UNSET_TC_FLOW_INFO = 116,
+ NBL_CHAN_MSG_INIT_ACL = 117,
+ NBL_CHAN_MSG_UNINIT_ACL = 118,
+ NBL_CHAN_MSG_CFG_LAG_MCC = 119,
+ NBL_CHAN_MSG_REGISTER_VSI2Q = 120,
+ NBL_CHAN_MSG_SETUP_Q2VSI = 121,
+ NBL_CHAN_MSG_REMOVE_Q2VSI = 122,
+ NBL_CHAN_MSG_SETUP_RSS = 123,
+ NBL_CHAN_MSG_REMOVE_RSS = 124,
+ NBL_CHAN_MSG_GET_REP_QUEUE_INFO = 125,
+ NBL_CHAN_MSG_CTRL_PORT_LED = 126,
+ NBL_CHAN_MSG_NWAY_RESET = 127,
+ NBL_CHAN_MSG_SET_INTL_SUPPRESS_LEVEL = 128,
+ NBL_CHAN_MSG_GET_ETH_STATS = 129,
+ NBL_CHAN_MSG_GET_MODULE_TEMPERATURE = 130,
+ NBL_CHAN_MSG_GET_BOARD_INFO = 131,
+ NBL_CHAN_MSG_GET_P4_USED = 132,
+ NBL_CHAN_MSG_GET_VF_BASE_VSI_ID = 133,
+ NBL_CHAN_MSG_ADD_LLDP_FLOW = 134,
+ NBL_CHAN_MSG_DEL_LLDP_FLOW = 135,
+ NBL_CHAN_MSG_CFG_ETH_BOND_INFO = 136,
+ NBL_CHAN_MSG_CFG_DUPPKT_MCC = 137,
+ NBL_CHAN_MSG_ADD_ND_UPCALL_FLOW = 138,
+ NBL_CHAN_MSG_DEL_ND_UPCALL_FLOW = 139,
+ NBL_CHAN_MSG_GET_BOARD_ID = 140,
+ NBL_CHAN_MSG_SET_SHAPING_DPORT_VLD = 141,
+ NBL_CHAN_MSG_SET_DPORT_FC_TH_VLD = 142,
+ NBL_CHAN_MSG_REGISTER_RDMA_BOND = 143,
+ NBL_CHAN_MSG_UNREGISTER_RDMA_BOND = 144,
+ NBL_CHAN_MSG_RESTORE_NETDEV_QUEUE = 145,
+ NBL_CHAN_MSG_RESTART_NETDEV_QUEUE = 146,
+ NBL_CHAN_MSG_RESTORE_HW_QUEUE = 147,
+ NBL_CHAN_MSG_KEEP_ALIVE = 148,
+ NBL_CHAN_MSG_GET_BASE_MAC_ADDR = 149,
+ NBL_CHAN_MSG_CFG_BOND_SHAPING = 150,
+ NBL_CHAN_MSG_CFG_BGID_BACK_PRESSURE = 151,
+ NBL_CHAN_MSG_ALLOC_KT_BLOCK = 152,
+ NBL_CHAN_MSG_FREE_KT_BLOCK = 153,
+ NBL_CHAN_MSG_GET_USER_QUEUE_INFO = 154,
+ NBL_CHAN_MSG_GET_ETH_BOND_INFO = 155,
+ NBL_CHAN_MSG_CLEAR_ACCEL_FLOW = 156,
+ NBL_CHAN_MSG_SET_BRIDGE_MODE = 157,
+ NBL_CHAN_MSG_GET_VF_FUNCTION_ID = 158,
+ NBL_CHAN_MSG_NOTIFY_LINK_FORCED = 159,
+ NBL_CHAN_MSG_SET_PMD_DEBUG = 160,
+ NBL_CHAN_MSG_REGISTER_FUNC_MAC = 161,
+ NBL_CHAN_MSG_SET_TX_RATE = 162,
+ NBL_CHAN_MSG_REGISTER_FUNC_LINK_FORCED = 163,
+ NBL_CHAN_MSG_GET_LINK_FORCED = 164,
+ NBL_CHAN_MSG_REGISTER_FUNC_VLAN = 165,
+ NBL_CHAN_MSG_GET_FD_FLOW = 166,
+ NBL_CHAN_MSG_GET_FD_FLOW_CNT = 167,
+ NBL_CHAN_MSG_GET_FD_FLOW_ALL = 168,
+ NBL_CHAN_MSG_GET_FD_FLOW_MAX = 169,
+ NBL_CHAN_MSG_REPLACE_FD_FLOW = 170,
+ NBL_CHAN_MSG_REMOVE_FD_FLOW = 171,
+ NBL_CHAN_MSG_CFG_FD_FLOW_STATE = 172,
+ NBL_CHAN_MSG_REGISTER_FUNC_RATE = 173,
+ NBL_CHAN_MSG_NOTIFY_VLAN = 174,
+ NBL_CHAN_MSG_GET_XDP_QUEUE_INFO = 175,
+ NBL_CHAN_MSG_STOP_ABNORMAL_SW_QUEUE = 176,
+ NBL_CHAN_MSG_STOP_ABNORMAL_HW_QUEUE = 177,
+ NBL_CHAN_MSG_NOTIFY_RESET_EVENT = 178,
+ NBL_CHAN_MSG_ACK_RESET_EVENT = 179,
+ NBL_CHAN_MSG_GET_VF_VSI_ID = 180,
+ NBL_CHAN_MSG_CONFIGURE_QOS = 181,
+ NBL_CHAN_MSG_GET_PFC_BUFFER_SIZE = 182,
+ NBL_CHAN_MSG_SET_PFC_BUFFER_SIZE = 183,
+ NBL_CHAN_MSG_GET_VF_STATS = 184,
+ NBL_CHAN_MSG_REGISTER_FUNC_TRUST = 185,
+ NBL_CHAN_MSG_NOTIFY_TRUST = 186,
+ NBL_CHAN_MSG_CHECK_VF_IS_ACTIVE = 187,
+ NBL_CHAN_MSG_GET_ETH_ABNORMAL_STATS = 188,
+ NBL_CHAN_MSG_GET_ETH_CTRL_STATS = 189,
+ NBL_CHAN_MSG_GET_PAUSE_STATS = 190,
+ NBL_CHAN_MSG_GET_ETH_MAC_STATS = 191,
+ NBL_CHAN_MSG_GET_FEC_STATS = 192,
+ NBL_CHAN_MSG_CFG_MULTI_MCAST_RULE = 193,
+ NBL_CHAN_MSG_GET_LINK_DOWN_COUNT = 194,
+ NBL_CHAN_MSG_GET_LINK_STATUS_OPCODE = 195,
+ NBL_CHAN_MSG_GET_RMON_STATS = 196,
+ NBL_CHAN_MSG_REGISTER_PF_NAME = 197,
+ NBL_CHAN_MSG_GET_PF_NAME = 198,
+ NBL_CHAN_MSG_CONFIGURE_RDMA_BW = 199,
+ NBL_CHAN_MSG_SET_RATE_LIMIT = 200,
+ NBL_CHAN_MSG_SET_TC_WGT = 201,
+ NBL_CHAN_MSG_REMOVE_QUEUE = 202,
+ NBL_CHAN_MSG_GET_MIRROR_TABLE_ID = 203,
+ NBL_CHAN_MSG_CONFIGURE_MIRROR = 204,
+ NBL_CHAN_MSG_CONFIGURE_MIRROR_TABLE = 205,
+ NBL_CHAN_MSG_CLEAR_MIRROR_CFG = 206,
+ NBL_CHAN_MSG_MIRROR_OUTPUTPORT_NOTIFY = 207,
+ NBL_CHAN_MSG_CHECK_FLOWTABLE_SPEC = 208,
+ NBL_CHAN_MSG_CHECK_VF_IS_VDPA = 209,
+ NBL_CHAN_MSG_GET_VDPA_VF_STATS = 210,
+ NBL_CHAN_MSG_SET_RX_RATE = 211,
+ NBL_CHAN_MSG_GET_UVN_PKT_DROP_STATS = 212,
+ NBL_CHAN_MSG_GET_USTORE_PKT_DROP_STATS = 213,
+ NBL_CHAN_MSG_GET_USTORE_TOTAL_PKT_DROP_STATS = 214,
+ NBL_CHAN_MSG_SET_WOL = 215,
+ NBL_CHAN_MSG_INIT_VF_MSIX_MAP = 216,
+ NBL_CHAN_MSG_GET_ST_NAME = 217,
+ /* mailbox msg end */
+ NBL_CHAN_MSG_MAILBOX_MAX,
+};
+
+#endif
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v18 00/13] Enable CXL PCIe Port Protocol Error handling and logging
From: Richard Cheng @ 2026-07-23 3:53 UTC (permalink / raw)
To: Terry Bowman
Cc: Bjorn Helgaas, Dan Williams, Dave Jiang, Ira Weiny,
Jonathan Cameron, Len Brown, Rafael J . Wysocki, Robert Richter,
linux-acpi, linux-cxl, linux-doc, linux-kernel, linux-pci,
linuxppc-dev, Alejandro Lucero, Alison Schofield, Ankit Agrawal,
Ard Biesheuvel, Ben Cheatham, Borislav Petkov, Breno Leitao,
Davidlohr Bueso, Fabio M . De Francesco, Gregory Price,
Hanjun Guo, Jonathan Corbet, Kees Cook,
Kuppuswamy Sathyanarayanan, Li Ming, Mahesh J Salgaonkar,
Mauro Carvalho Chehab, Oliver O'Halloran, Shiju Jose,
Shuah Khan, Shuai Xue, Smita Koralahalli, Tony Luck, Vishal Verma
In-Reply-To: <20260717222706.3540281-1-terry.bowman@amd.com>
On Fri, Jul 17, 2026 at 05:26:53PM +0800, Terry Bowman wrote:
>
> This patch series enables CXL protocol error handling for both CXL Ports
> and CXL Endpoints (EP). The previous revision is available at:
>
> https://lore.kernel.org/linux-cxl/20260505173029.2718246-1-terry.bowman@amd.com/
>
> Today the kernel handles native CXL.cachemem RAS only for Endpoints and
> Restricted CXL Host (RCH) Downstream Ports. Root Ports, Upstream Switch
> Ports, and Downstream Switch Ports are uncovered. This series introduces
> a unified CXL protocol error path for all CXL device types, in both VH
> and RCH topologies.
>
> CXL protocol errors are layered as a distinct error plane on top of PCIe
> AER. CXL RAS conditions are signaled as PCIe correctable (CE) and
> uncorrectable (UCE) Internal AER Errors. The AER driver classifies these
> events using pcie_is_cxl() and hands them off to cxl_core through the
> AER-CXL kfifo.
>
> The cxl_core driver dequeues each event, resolves the cxl_port topology,
> and dispatches to the CE or UCE handler. RCD Endpoints are handled
> slightly differently: the RCH Downstream Port's RAS state is processed
> first, then the Endpoint's own RAS follows the common path.
>
> PCIe AER errors remain a separate plane and are handled independently.
> This series updates the CXL Endpoint AER UCE handler and removes the
> Endpoint AER CE handler, which is now redundant since the AER driver
> clears and logs CE status itself.
>
Hi Terry,
From the first glance after I read the cover letter, this part make me
thought you are seperating CXL CE and CXL UCE, the former put into PCIe AER,
which would be a surprising split for me.
But from the following patches I don't think that's your intent ?
For the next version can you tweak this part to say the Endpoint CE callback
is removed as duplicate with common CXL protocol-error path, rathter than
saying CXL CE is delegated to AER ?
That would saved me a false start.
> PCI_ERS_RESULT_PANIC, introduced in earlier revisions, has been dropped.
> The panic decision is made directly in cxl_do_recovery(): the kernel
> panics on any uncorrectable CXL RAS error reported by cxl_handle_ras(),
> or earlier on link disconnect.
>
Sounds good.
> A fatal UCE on an Upstream Switch Port or Endpoint surfaces through the
> AER path rather than the CXL RAS path. USP devices are bound to the PCIe
> portdrv driver, so when a USP reports a fatal UCE, the PCIe error handler
> provided by portdrv is invoked. PCI config reads to the source device are
> expected to fail in this scenario, so the AER core never retrieves
> UNCOR_STATUS, and the event cannot be classified as CXL. See the fatal
> and non-fatal log excerpts for USP and EP below.
>
> == Patch Details ==
>
> Patch 1 - cxl/ras: Fix cxl_rch_get_aer_severity() wrong severity register
> Fix RCH Downstream Port severity classification. Was using
> PCI_ERR_ROOT_FATAL_RCV (a Root Error Status bit) instead of uncor_severity
> to classify fatal vs non-fatal.
>
> Patch 2 - acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks
> Convert cxl_cper_work_lock and cxl_cper_prot_err_work_lock from spinlock_t
> to raw_spinlock_t to avoid sleeping-in-hardirq deadlock on PREEMPT_RT kernels.
>
> Patch 3 - cxl: Tighten CPER kfifo registration API and symbol visibility
> Replace EXPORT_SYMBOL_NS_GPL() with EXPORT_SYMBOL_FOR_MODULES() for CPER
> kfifo helpers. Simplify register/unregister return types to void.
>
> Patch 4 - cxl: Rename find_cxl_port() to find_cxl_port_by_dport()
> Renames find_cxl_port() to find_cxl_port_by_dport() to make the lookup
> method explicit and consistent with the existing find_cxl_port_by_uport().
>
> Patch 5 - PCI/AER: Introduce AER-CXL protocol error kfifo
> Adds the AER-CXL kfifo in drivers/pci/pcie/aer_cxl_vh.c along with the
> producer helper cxl_forward_error() and the consumer registration helpers
> cxl_register_proto_err_work() and cxl_unregister_proto_err_work(). The
> kfifo delivers CXL VH protocol errors from the AER driver to cxl_core.
>
> Patch 6 - PCI: Establish common CXL Port protocol error flow
> Dequeues work from the AER-CXL kfifo and establishes a common flow for all
> CXL Port protocol error handling. Panics on any uncorrectable CXL RAS
> error. The producer dispatch and consumer go live atomically.
>
> Patch 7 - PCI/CXL: Add RCH support to CXL handlers
> Folds Restricted CXL Host (RCH) error handling into the common Port flow. An
> RCD uncorrectable CXL RAS error now panics, matching the policy applied to
> all other CXL devices.
>
> Patch 8 - cxl/pci: Thread port and dport through RAS handling helpers
> Refactors cxl_handle_ras() and cxl_handle_cor_ras() to accept struct
> cxl_port * and struct cxl_dport * directly, eliminating redundant bus walks on
> the error path.
>
> Patch 9 - cxl: Update CXL Endpoint AER handler
> Replaces cxl_error_detected() with cxl_pci_error_detected(). Reads CXL RAS
> unconditionally; panics on UCE regardless of channel state.
>
> Patch 10 - cxl: Add port and dport identifiers to CXL AER trace events
> Passes struct cxl_port * and struct cxl_dport * to trace events. Adds new
> "port" and "dport" string fields for all CXL device classes.
>
> Patch 11 - PCI: Cache PCI DSN into pci_dev->dsn during probe
> Caches the PCI Device Serial Number at probe time so error handlers and
> panic paths avoid live config-space reads.
>
> Patch 12 - PCI/CXL: Mask/Unmask CXL protocol errors
> Enables CXL Internal Error reporting on CXL Ports and Endpoints. The unmask
> is paired with RAS register block mapping; the mask is registered as a devres
> action.
>
> Patch 13 - Documentation: cxl: Document CXL protocol error handling
> Adds protocol-error-handling.rst describing the end-to-end CXL protocol
> error path.
>
> == Notes ==
>
> - @Bjorn, I kindly request your review for the following patches. Many
> of the changes are to CXL-specific files in the PCI tree:
> Patch 5 - PCI/AER: Introduce AER-CXL protocol error kfifo
> Patch 6 - PCI: Establish common CXL Port protocol error flow
> Patch 7 - PCI/CXL: Add RCH support to CXL handlers
> Patch 11 - PCI: Cache PCI DSN into pci_dev->dsn during probe
> Patch 12 - PCI/CXL: Mask/Unmask CXL protocol errors
>
> - USP/EP fatal UCE follows the AER path because of how the AER core collects
> status. aer_get_device_error_info() only reads PCI_ERR_UNCOR_STATUS for
> Root Ports/RCECs/Downstream Ports or non-fatal severities, where config
> reads to the source are still expected to succeed. For a fatal UCE
> signaled by an upstream component, config reads to that device are
> expected to fail, so UNCOR_STATUS is never retrieved. Without the status
> word, is_cxl_error() cannot classify the event as CXL and the AER path
> handles it.
>
> - Dan's related series addressing RAS setup has more details:
> https://lore.kernel.org/linux-cxl/20260131000403.2135324-1-dan.j.williams@intel.com/
>
> - TODOs for future series:
> - Move aer_cxl_rch.c to cxl/core/ras_rch.c
> - Move RCH traversing for handling from AER driver into CXL driver
> - Support user-defined status masks
> - Add CXL Port traversing in cxl_do_recovery()
>
> == Testing ==
>
> Testing included the following:
> - cxl-test
> - RCH/RCD AER error injection
> - CPER GHES (firmware first)
> - VH AER error injection
>
> ** The AER error injection is not included in this series but will be posted
> as an RFC for review and for others to use. The error injection using AER
> will be posted separately as ("cxl: Device protocol AER injection").
>
> Below are the testing results.
>
> === cxl_test ===
> The cxl_test CXL testsuite passed on QEMU with no issues.
>
> This required changes in patch 12 ("PCI/CXL: Mask/Unmask CXL protocol errors").
> __wrap_devm_cxl_dport_rch_ras_setup() is introduced to prevent cxl_test from
> trying to map the RCH AER/RAS registers.
>
> === CPER Tests ===
> CPER/firmware first error injection was run and passed on real HW using AMD
> RAS tool for protocol error injection at the CXL Root Port.
>
> ==== Restricted CXL Host (RCH) ====
> Error injection tests for RCH devices were run using CXL2.0 Endpoint that enumerate
> as a RCiEP.
>
> echo "0000:7f:00.0 CE 00000000 00000002 RCH" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:40:00.3: aer_inject: Injecting errors 00004000/00000000 into device 0000:40:00.3
> pcieport 0000:40:00.3: AER: Correctable error message received from 0000:40:00.3
> pcieport 0000:40:00.3: PCIe Bus Error: severity=Correctable, type=Transaction Layer, (Receiver ID)
> pcieport 0000:40:00.3: device [1022:14a6] error status/mask=00004000/00002000
> pcieport 0000:40:00.3: [14] CorrIntErr
> cxl_aer_correctable_error: memdev= port=root0 dport=pci0000:7f host=ACPI0017:00 serial=0: status: 'Memory Data ECC Error'
>
> echo "0000:7f:00.0 UCE 00000000 00000002 RCH" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:40:00.3: aer_inject: Injecting errors 00000000/00400000 into device 0000:40:00.3
> pcieport 0000:40:00.3: AER: Uncorrectable (Non-Fatal) error message received from 0000:40:00.3
> pcieport 0000:40:00.3: PCIe Bus Error: severity=Uncorrectable (Non-Fatal), type=Transaction Layer, (Receiver ID)
> pcieport 0000:40:00.3: device [1022:14a6] error status/mask=00400000/00000000
> pcieport 0000:40:00.3: [22] UncorrIntErr
> cxl_aer_uncorrectable_error: memdev= port=root0 dport=pci0000:7f host=ACPI0017:00 serial=0: status: 'Cache Address Parity Error' first_error: 'Cache Address Parity Error'
> Kernel panic - not syncing: CXL cachemem error
> CPU: 26 UID: 0 PID: 396 Comm: kworker/26:1 Kdump: loaded Not tainted 7.2.0-rc3-tb-00014-g6346be30306a #1363 PREEMPT(lazy)
> Hardware name: AMD Corporation ONYX/ONYX, BIOS TOX100HB 12/03/2025
> Workqueue: events cxl_proto_err_work_fn [cxl_core]
> Call Trace:
> <TASK>
> vpanic+0x453/0x4b0
> panic+0x56/0x60
> cxl_do_recovery+0x66/0x70 [cxl_core]
> cxl_handle_rdport_errors+0x176/0x190 [cxl_core]
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? update_load_avg+0x5c/0x2b0
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? dequeue_entities+0x160/0xb40
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? pick_task_fair+0x164/0x670
> ? __pfx___cxl_proto_err_work_fn+0x10/0x10 [cxl_core]
> __cxl_proto_err_work_fn+0xea/0x1b0 [cxl_core]
> ? __pfx___cxl_proto_err_work_fn+0x10/0x10 [cxl_core]
> for_each_cxl_proto_err+0x5a/0x80
> cxl_proto_err_work_fn+0x26/0x50 [cxl_core]
> process_one_work+0x16e/0x3a0
> worker_thread+0x172/0x2e0
> ? __pfx_worker_thread+0x10/0x10
> kthread+0xe5/0x120
> ? __pfx_kthread+0x10/0x10
> ret_from_fork+0x1bd/0x220
> ? __pfx_kthread+0x10/0x10
> ret_from_fork_asm+0x1a/0x30
> </TASK>
>
> ==== Restricted CXL Device (RCD) ====
> Error injection tests for RCD devices were run using CXL2.0 Endpoint that enumerate
> as a RCiEP.
>
> echo "0000:7f:00.0 CE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:40:00.3: aer_inject: Injecting errors 00004000/00000000 into device 0000:40:00.3
> pcieport 0000:40:00.3: AER: Correctable error message received from 0000:40:00.3
> pcieport 0000:40:00.3: PCIe Bus Error: severity=Correctable, type=Transaction Layer, (Receiver ID)
> pcieport 0000:40:00.3: device [1022:14a6] error status/mask=00004000/00002000
> pcieport 0000:40:00.3: [14] CorrIntErr
> cxl_aer_correctable_error: memdev=mem0 port=endpoint1 dport= host=0000:7f:00.0 serial=0: status: 'Memory Data ECC Error'
>
> echo "0000:7f:00.0 UCE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:40:00.3: aer_inject: Injecting errors 00000000/00400000 into device 0000:40:00.3
> pcieport 0000:40:00.3: AER: Uncorrectable (Non-Fatal) error message received from 0000:40:00.3
> pcieport 0000:40:00.3: PCIe Bus Error: severity=Uncorrectable (Non-Fatal), type=Transaction Layer, (Receiver ID)
> pcieport 0000:40:00.3: device [1022:14a6] error status/mask=00400000/00000000
> pcieport 0000:40:00.3: [22] UncorrIntErr
> cxl_aer_uncorrectable_error: memdev=mem0 port=endpoint1 dport= host=0000:7f:00.0 serial=0: status: 'Cache Address Parity Error' first_error: 'Cache Address Parity Error'
> Kernel panic - not syncing: CXL cachemem error
> CPU: 26 UID: 0 PID: 394 Comm: kworker/26:1 Kdump: loaded Not tainted 7.2.0-rc3-tb-00014-g6346be30306a #1363 PREEMPT(lazy)
> Hardware name: AMD Corporation ONYX/ONYX, BIOS TOX100HB 12/03/2025
> Workqueue: events cxl_proto_err_work_fn [cxl_core]
> Call Trace:
> <TASK>
> vpanic+0x453/0x4b0
> panic+0x56/0x60
> cxl_do_recovery+0x66/0x70 [cxl_core]
> __cxl_proto_err_work_fn+0xa0/0x1b0 [cxl_core]
> ? __pfx___cxl_proto_err_work_fn+0x10/0x10 [cxl_core]
> for_each_cxl_proto_err+0x5a/0x80
> cxl_proto_err_work_fn+0x26/0x50 [cxl_core]
> process_one_work+0x16e/0x3a0
> worker_thread+0x172/0x2e0
> ? __pfx_worker_thread+0x10/0x10
> kthread+0xe5/0x120
> ? __pfx_kthread+0x10/0x10
> ret_from_fork+0x1bd/0x220
> ? __pfx_kthread+0x10/0x10
> ret_from_fork_asm+0x1a/0x30
> </TASK>
>
> === Virtual Hierarchy ===
> Below are the VH error injection test results using QEMU with CXL AER error
> injection via /sys/kernel/debug/cxl/aer_einj_inject on kernel 7.2.0-rc3 based
> kernel.
> The below QEMU testing uses a CXL Root Port, a CXL Upstream Switch Port, a
> CXL Downstream Switch Port, and a CXL Type3 Endpoint as given below.
>
> The sub-topology for the QEMU testing is:
>
> ---------------------
> | CXL RP - 0C:00.0 |
> ---------------------
> |
> ---------------------
> | CXL USP - 0D:00.0 |
> ---------------------
> |
> --------------------
> | CXL DSP - 0E:00.0 |
> --------------------
> |
> ---------------------
> | CXL EP - 0F:00.0 |
> ---------------------
>
> Error Injection Test Results Summary:
>
> | # | Device Type | BDF | Test | Result | AER | RAS | Verdict |
> |---|------------------|---------|------|-----------|-----|-----|------------------|
> | 1 | Root Port | 0c:00.0 | CE | Recovered | OK | OK | PASS |
> | 2 | Root Port | 0c:00.0 | UCE | Panic | OK | OK | PASS |
> | 3 | Upstream Port | 0d:00.0 | CE | Recovered | OK | OK | PASS |
> | 4 | Upstream Port | 0d:00.0 | UCE | Recovered | OK | -- | PASS (Known Lim) |
> | 5 | Downstream Port | 0e:00.0 | CE | Recovered | OK | OK | PASS |
> | 6 | Downstream Port | 0e:00.0 | UCE | Panic | OK | OK | PASS |
> | 7 | Endpoint | 0f:00.0 | CE | Recovered | OK | OK | PASS |
> | 8 | Endpoint | 0f:00.0 | UCE | Panic | OK | OK | PASS |
>
> Overall: 8/8 PASS
>
> === Root Port - CE ===
>
> echo "0000:0c:00.0 CE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:0c:00.0: aer_inject: Injecting errors 00004000/00000000 into device 0000:0c:00.0
> pcieport 0000:0c:00.0: AER: Correctable error message received from 0000:0c:00.0
> pcieport 0000:0c:00.0: CXL Bus Error: severity=Correctable, type=Transaction Layer, (Receiver ID)
> pcieport 0000:0c:00.0: device [8086:7075] error status/mask=00004000/0000a000
> pcieport 0000:0c:00.0: [14] CorrIntErr
> cxl_aer_correctable_error: memdev= port=port1 dport=0000:0c:00.0 host=pci0000:0c serial=0: status: 'Memory Data ECC Error'
>
> === Root Port - UCE ===
>
> echo "0000:0c:00.0 UCE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:0c:00.0: aer_inject: Injecting errors 00000000/00400000 into device 0000:0c:00.0
> pcieport 0000:0c:00.0: AER: Uncorrectable (Fatal) error message received from 0000:0c:00.0
> pcieport 0000:0c:00.0: CXL Bus Error: severity=Uncorrectable (Fatal), type=Transaction Layer, (Receiver ID)
> pcieport 0000:0c:00.0: device [8086:7075] error status/mask=00400000/02000000
> pcieport 0000:0c:00.0: [22] UncorrIntErr
> cxl_aer_uncorrectable_error: memdev= port=port1 dport=0000:0c:00.0 host=pci0000:0c serial=0: status: 'Cache Address Parity Error' first_error: 'Cache Address Parity Error'
> Kernel panic - not syncing: CXL cachemem error
> CPU: 58 UID: 0 PID: 409 Comm: kworker/58:1 Not tainted 7.2.0-rc3-tb-00014-g23418142f421 #1291 PREEMPT(lazy)
> Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
> Workqueue: events cxl_proto_err_work_fn [cxl_core]
> Call Trace:
> <TASK>
> vpanic+0x453/0x4b0
> panic+0x56/0x60
> cxl_do_recovery+0x66/0x70 [cxl_core]
> __cxl_proto_err_work_fn+0x9e/0x1b0 [cxl_core]
> ? __pfx___cxl_proto_err_work_fn+0x10/0x10 [cxl_core]
> for_each_cxl_proto_err+0x5a/0x80
> cxl_proto_err_work_fn+0x26/0x50 [cxl_core]
> process_one_work+0x16e/0x3a0
> worker_thread+0x172/0x2e0
> ? __pfx_worker_thread+0x10/0x10
> kthread+0xe5/0x120
> ? __pfx_kthread+0x10/0x10
> ret_from_fork+0x1bd/0x220
> ? __pfx_kthread+0x10/0x10
> ret_from_fork_asm+0x1a/0x30
> </TASK>
> Kernel Offset: disabled
> ---[ end Kernel panic - not syncing: CXL cachemem error ]---
>
> === Upstream Switch Port - CE ===
>
> echo "0000:0d:00.0 CE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:0c:00.0: aer_inject: Injecting errors 00004000/00000000 into device 0000:0d:00.0
> pcieport 0000:0c:00.0: AER: Correctable error message received from 0000:0d:00.0
> pcieport 0000:0d:00.0: CXL Bus Error: severity=Correctable, type=Transaction Layer, (Receiver ID)
> pcieport 0000:0d:00.0: device [19e5:a128] error status/mask=00004000/0000a000
> pcieport 0000:0d:00.0: [14] CorrIntErr
> cxl_aer_correctable_error: memdev= port=port2 dport= host=0000:0d:00.0 serial=0: status: 'Memory Data ECC Error'
>
> === Upstream Switch Port - UCE (fatal - AER recovery, known limitation) ===
>
> echo "0000:0d:00.0 UCE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:0c:00.0: aer_inject: Injecting errors 00000000/00400000 into device 0000:0d:00.0
> pcieport 0000:0c:00.0: AER: Uncorrectable (Fatal) error message received from 0000:0d:00.0
> pcieport 0000:0d:00.0: AER: CXL Bus Error: severity=Uncorrectable (Fatal), type=Inaccessible, (Unregistered Agent ID)
> cxl_pci 0000:0f:00.0: mem0: frozen state error detected, disable CXL.mem
> pcieport 0000:0c:00.0: AER: Root Port link has been reset (0)
> cxl_pci 0000:0f:00.0: mem0: restart CXL.mem after slot reset
> cxl_pci 0000:0f:00.0: mem0: error resume successful
> pcieport 0000:0c:00.0: AER: device recovery successful
>
> === Downstream Port - CE ===
>
> echo "0000:0e:00.0 CE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:0c:00.0: aer_inject: Injecting errors 00004000/00000000 into device 0000:0e:00.0
> pcieport 0000:0c:00.0: AER: Correctable error message received from 0000:0e:00.0
> pcieport 0000:0e:00.0: CXL Bus Error: severity=Correctable, type=Transaction Layer, (Receiver ID)
> pcieport 0000:0e:00.0: device [19e5:a129] error status/mask=00004000/0000a000
> pcieport 0000:0e:00.0: [14] CorrIntErr
> cxl_aer_correctable_error: memdev= port=port2 dport=0000:0e:00.0 host=0000:0d:00.0 serial=0: status: 'Memory Data ECC Error'
>
> === Downstream Port - UCE ===
>
> echo "0000:0e:00.0 UCE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:0c:00.0: aer_inject: Injecting errors 00000000/00400000 into device 0000:0e:00.0
> pcieport 0000:0c:00.0: AER: Uncorrectable (Fatal) error message received from 0000:0e:00.0
> pcieport 0000:0e:00.0: CXL Bus Error: severity=Uncorrectable (Fatal), type=Transaction Layer, (Receiver ID)
> pcieport 0000:0e:00.0: device [19e5:a129] error status/mask=00400000/02000000
> pcieport 0000:0e:00.0: [22] UncorrIntErr
> cxl_aer_uncorrectable_error: memdev= port=port2 dport=0000:0e:00.0 host=0000:0d:00.0 serial=0: status: 'Cache Address Parity Error' first_error: 'Cache Address Parity Error'
> Kernel panic - not syncing: CXL cachemem error
> CPU: 7 UID: 0 PID: 299 Comm: kworker/7:1 Not tainted 7.2.0-rc3-tb-00014-g832c50e87f10 #1364 PREEMPT(lazy)
> Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
> Workqueue: events cxl_proto_err_work_fn [cxl_core]
> Call Trace:
> <TASK>
> vpanic+0x453/0x4b0
> panic+0x56/0x60
> cxl_do_recovery+0x66/0x70 [cxl_core]
> __cxl_proto_err_work_fn+0xa0/0x1b0 [cxl_core]
> ? __pfx___cxl_proto_err_work_fn+0x10/0x10 [cxl_core]
> for_each_cxl_proto_err+0x5a/0x80
> cxl_proto_err_work_fn+0x26/0x50 [cxl_core]
> process_one_work+0x16e/0x3a0
> worker_thread+0x172/0x2e0
> ? __pfx_worker_thread+0x10/0x10
> kthread+0xe5/0x120
> ? __pfx_kthread+0x10/0x10
> ret_from_fork+0x1bd/0x220
> ? __pfx_kthread+0x10/0x10
> ret_from_fork_asm+0x1a/0x30
> </TASK>
> Kernel Offset: disabled
> ---[ end Kernel panic - not syncing: CXL cachemem error ]---
>
> === Endpoint - CE ===
>
> echo "0000:0f:00.0 CE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:0c:00.0: aer_inject: Injecting errors 00004000/00000000 into device 0000:0f:00.0
> pcieport 0000:0c:00.0: AER: Correctable error message received from 0000:0f:00.0
> cxl_pci 0000:0f:00.0: CXL Bus Error: severity=Correctable, type=Transaction Layer, (Receiver ID)
> cxl_pci 0000:0f:00.0: device [8086:0d93] error status/mask=00004000/0000a000
> cxl_pci 0000:0f:00.0: [14] CorrIntErr
> cxl_aer_correctable_error: memdev=mem1 port=endpoint4 dport= host=0000:0f:00.0 serial=0: status: 'Memory Data ECC Error'
>
> === Endpoint - UCE ===
>
> echo "0000:0f:00.0 UCE 00000000 00000002" > /sys/kernel/debug/cxl/aer_einj_inject
>
> pcieport 0000:0c:00.0: aer_inject: Injecting errors 00000000/00400000 into device 0000:0f:00.0
> pcieport 0000:0c:00.0: AER: Uncorrectable (Fatal) error message received from 0000:0f:00.0
> cxl_pci 0000:0f:00.0: AER: CXL Bus Error: severity=Uncorrectable (Fatal), type=Inaccessible, (Unregistered Agent ID)
> cxl_aer_uncorrectable_error: memdev=mem1 port=endpoint4 dport= host=0000:0f:00.0 serial=0: status: 'Cache Address Parity Error' first_error: 'Cache Address Parity Error'
> Kernel panic - not syncing: CXL cachemem error
> CPU: 58 UID: 0 PID: 430 Comm: irq/24-aerdrv Not tainted 7.2.0-rc3-tb-00014-g23418142f421 #1291 PREEMPT(lazy)
> Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
> Call Trace:
> <TASK>
> vpanic+0x453/0x4b0
> panic+0x56/0x60
> cxl_pci_error_detected+0x15c/0x160 [cxl_core]
> report_error_detected+0xc7/0x1c0
> ? __pfx_report_frozen_detected+0x10/0x10
> __pci_walk_bus+0x47/0x70
> ? __pfx_report_frozen_detected+0x10/0x10
> pci_walk_bus+0x2c/0x40
> ? __pfx_aer_root_reset+0x10/0x10
> pcie_do_recovery+0x234/0x330
> ? __pfx_irq_thread_fn+0x10/0x10
> aer_isr_one_error_type+0x333/0x340
> aer_isr_one_error+0x112/0x140
> aer_isr+0x47/0x80
> irq_thread_fn+0x1f/0x60
> irq_thread+0x123/0x220
> ? __pfx_irq_thread_dtor+0x10/0x10
> ? __pfx_irq_thread+0x10/0x10
> kthread+0xe5/0x120
> ? __pfx_kthread+0x10/0x10
> ret_from_fork+0x1bd/0x220
> ? __pfx_kthread+0x10/0x10
> ret_from_fork_asm+0x1a/0x30
> </TASK>
> Kernel Offset: disabled
> ---[ end Kernel panic - not syncing: CXL cachemem error ]---
>
>
> == Changes ==
>
> Changes in v17->v18:
> acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks
> - New commit
> - Convert cxl_cper_work_lock and cxl_cper_prot_err_work_lock from
> spinlock_t to raw_spinlock_t; use guard(raw_spinlock_irqsave) at
> all call sites to avoid sleeping-in-hardirq on PREEMPT_RT.
> - Add kfifo_reset(&cxl_cper_fifo) to cxl_cper_unregister_work()
> - Add WARN_ONCE to cxl_cper_register_work() for double-registration consistency
> - Fix cxl_cper_unregister_work() to clear global pointer before cancel_work_sync()
> - Remove redundant cancel_work_sync() from cxl_ras_exit()
> cxl: Tighten CPER kfifo registration API and symbol visibility
> - New commit (split/rework of v17 "Limit CXL-CPER kfifo registration functions scope")
> cxl: Rename find_cxl_port() to find_cxl_port_by_dport()
> - None
> PCI/AER: Introduce AER-CXL protocol error kfifo
> - Remove correctable status clear from cxl_forward_error(); the AER core
> clears all status bits via pci_aer_handle_error() info->status writeback
> - Schedule consumer on kfifo overflow so existing entries can be drained
> PCI: Establish common CXL Port protocol error flow
> - Fix handle_error_source() to call pci_aer_handle_error() unconditionally
> so AER handling always runs after cxl_forward_error()
> - Add cxl_proto_err_flush() call for CXL UCE to drain kfifo before AER
> recovery tears down the device
> - Fix NULL dereference of dport->dport_dev in cxl_handle_cor_ras() and
> cxl_handle_ras() for UPSTREAM/ENDPOINT port types: use dport->dport_dev
> when dport is non-NULL, else fall back to port->uport_dev
> - Remove duplicate pcie_clear_device_status() call from
> cxl_handle_proto_error() CE path; pci_aer_handle_error() already clears it
> - Clarify panic policy: panic only on confirmed UCE via RAS status read
> - Document kfifo consumer serialization against driver unbind via
> guard(device)(&port->dev) and port->dev.driver check
> PCI/CXL: Add RCH support to CXL handlers
> - Pass &pdev->dev instead of dport->port->uport_dev in
> cxl_handle_rdport_errors() to avoid dropping RCH trace events.
> - Document trace event attribution change.
> - Document removal of cxl_cor_error_detected() and cxlds->rcd branches.
> - Capitalize Endpoint per PCI spec convention.
> - Use to_ras_base() in cxl_handle_rdport_errors() to centralize RAS base
> address lookup in preparation for error injection testing.
> cxl/pci: Thread port and dport through RAS handling helpers
> - New commit
> cxl: Update CXL Endpoint AER handler
> - Fix cxl_pci_error_detected() to use find_cxl_port_by_uport() and port->uport_dev
> - Read CXL RAS unconditionally; panic on UCE regardless of channel state
> - Document unconditional read policy and 0xFFFFFFFF behavior in comment
> cxl: Add port and dport identifiers to CXL AER trace events
> - Consolidate double find_cxl_port_by_dev() in cxl_cper_handle_prot_err()
> - Add comment noting dport is NULL for endpoint and upstream port devices
> PCI: Cache PCI DSN into pci_dev->dsn during probe
> - New commit
> PCI/CXL: Mask/Unmask CXL protocol errors
> - Make cxl_unmask_proto_interrupts() and cxl_mask_proto_interrupts() static
> - Remove dev_is_pci() guard from devm_cxl_dport_rch_ras_setup(); the guard
> blocked real RCH hardware because pci_host_bridge is not on pci_bus_type
> Documentation: cxl: Document CXL protocol error handling
> - Simplify document for readability (Jonathan)
> - Drop historical context that goes stale (Jonathan)
> - Shorten ASCII flow diagram (Jonathan)
> - Drop manual backtick markup, use automarkup (Jonathan)
> - Clarify USP/DSP as single switch component (Dave)
> - Fix line wrapping to 80 chars (Jonathan)
>
> Changes in v16->v17:
> PCI/AER: Introduce AER-CXL Kfifo
> - Reword "kfifo semaphore" to "kfifo spinlock" to match fifo_lock.
> - Defer the handle_error_source() is_cxl_error() switch to the patch that
> registers the kfifo consumer to keep each commit bisect-safe.
> - Rename rwsema to rwsem
> - Change CPER exports to use EXPORT_SYMBOL_FOR_MODULES.
> - Add work cancel function.
> - Replace kfifo_put() with kfifo_in_spinlocked() for multiple producers
> - Add fifo_lock spinlock for concurrent producer serialisation
> - Initialize the embedded kfifo with INIT_KFIFO() in a subsys_initcall so
> kfifo->mask, ->esize and ->data are set before first use.
> - Clear PCI_ERR_COR_STATUS in cxl_forward_error() before enqueue so the
> device is acked for correctable events even when the consumer drops the
> event. Uncorrectable status is left for cxl_do_recovery() to clear after
> recovery completes, mirroring the AER core convention.
> - WARN on double-registration in cxl_register_proto_err_work() to make an
> unintended second consumer visible at runtime.
> - Add direct rwsem.h, cleanup.h and workqueue.h includes for symbols used
> in aer_cxl_vh.c
> - Add MAINTAINERS entries for drivers/pci/pcie/aer_cxl_*.c
> - Update message
> cxl/ras: Unify Endpoint and Port AER trace events
> - Replace cxlds->serial with pci_get_dsn()
> - Change 'memdev' to 'device' (Dan)
> - Updated Commit message
> cxl: Use common CPER handling for all CXL devices
> - New commit
> cxl: Rename find_cxl_port() to find_cxl_port_by_dport()
> - New commit
> - Drop the de-staticisation of find_cxl_port_by_uport() and the
> core.h declarations from this prep patch; both move to the patch
> that introduces the first cross-file caller.
> cxl: Limit CXL-CPER kfifo registration functions scope
> - Split from v16 02/10 ("Update unregistration for AER-CXL and
> CPER-CXL kfifos"); AER-CXL half folded into v17 01/10.
> - Convert exports to EXPORT_SYMBOL_FOR_MODULES("cxl_core").
> - Change register/unregister return type from int to void.
> - Drop work_struct argument from cxl_cper_unregister_prot_err_work();
> it now cancels its own work.
> - Remove now-redundant cancel_work_sync() from cxl_ras_exit().
> - Add WARN_ONCE() in cxl_cper_register_prot_err_work() for
> double-registration.
> PCI: Establish common CXL Port protocol error flow
> - get_cxl_port() -> find_cxl_port_by_dev()
> - Simplified find_cxl_port_by_dev()
> - Replace and remove cxl_serial_number() w/ pci_get_dsn()
> - cxl_get_ras_base() -> to_ras_base()
> - Drop dependency on PCI_ERS_RESULT_PANIC; cxl_do_recovery() panics
> directly. (PANIC enum patch dropped from series.)
> - Clarify panic semantics: panic on any uncorrectable CXL RAS error, not
> only AER-FATAL severities.
> - Drop the redundant PCI_ERR_COR_STATUS RMW in cxl_handle_proto_error();
> cxl_forward_error() already acks the correctable AER status.
> - Add is_cxl_error() switch in handle_error_source() here, paired with the
> kfifo consumer registration, to keep each commit bisect-safe.
> - Drop pcie_aer_is_native() guard in cxl_do_recovery() (always native).
> - Swap order with the "Limit" patch for bisectability w/ cxl_ras_exit()
> - Reword for "any uncorrectable" CXL RAS error panics.
> - Restore log messages for port-not-found and port-unbound cases.
> - Whitespace cleanup (Jonathan)
> - Update to get_cxl_port() documentation (Terry)
> - Fix __cxl_proto_err_work_fn() to return 0 for transient errors.
> - Drop !port check in cxl_do_recovery(), caller already validated
> - Fix kerneldoc @pdev -> @dev in find_cxl_port_by_dev()
> - Fix missing space in pr_err_ratelimited()
> - Add disconnect check before access
> - Made pcie_clear_device_status() and pci_aer_clear_fatal_status()
> EXPORT_SYMBOL_FOR_MODULES("cxl_core") (Dan)
> - Move find_cxl_port_by_dport() and find_cxl_port_by_uport()
> de-staticisation and core.h declarations from the rename patch to
> here, where the first cross-file callers in find_cxl_port_by_dev()
> land.
> PCI/CXL: Add RCH support to CXL handlers
> - Drop now-dead cxlds->rcd branches from cxl_{cor_,}error_detected().
> - Drop duplicate subject line from commit body.
> - Document panic-on-uncorrectable behavior change for RCD path.
> - Document trace event device-name change (memN -> PCI BDF) for RCH path.
> - Rewrite cxl_handle_proto_error() RC_END comment to clarify RCD/RCH shared
> interrupt relationship
> - Rewrite commit message
> cxl: Remove Endpoint AER correctable handler
> - Update commit message
> - Add Reviewed-by from Jonathan and DaveJ
> cxl: Update Endpoint AER uncorrectable handler
> - Rename pci_error_handlers struct instance to cxl_pci_error_handlers to
> avoid shadowing the struct type tag.
> - Restore scoped_guard(device) and dev->driver check around AER read.
> - NULL-check find_cxl_port_by_dev() before deref of port->uport_dev.
> - Updated commit message. (Terry)
> - Add scope cleanup for port variable in cxl_pci_error_detected() (Terry)
> - Drop cxl_uncor_aer_present(), rely on AER state
> PCI/CXL: Mask/Unmask CXL protocol errors
> - Drop redundant cxl_mask_proto_interrupts() calls from unregister_port()
> and cxl_dport_remove(); the devres action registered alongside the unmask
> is the sole mask path.
> - Update title
> - Remove unnecessary check for aer_capabilities
> - Gate cxl_unmask_proto_interrupts() on pcie_aer_is_native()
> - Add pci_aer_mask_internal_errors() and cxl_mask_proto_interrupts()
> - Only unmask on successful cxl_map_component_regs()
> - NULL-check @dev in cxl_{un,}mask_proto_interrupts()
> - Drop static and declare in core/core.h
> Documentation: cxl: Document CXL protocol error handling
> - New commit
>
> Changes in v15->v16:
> PCI/AER: Introduce AER-CXL Kfifo
> - Add pci_dev_put() and comment at pci_dev_get() (Dan)
> - /rw_sema/rwsema/ (Dan)
> - Split validation checks in cxl_forward_error() to allow
> for meaningful reason in log (Terry)
> - Shortened commit title to remove wordiness (Terry)
> PCI/CXL: Update unregistration for AER-CXL and CPER-CXL kfifos
> - New commit
> cxl: Update CXL Endpoint tracing
> - Add Dan's review-by
> - Incorporate Dan's comment into commit message:
> "Add the serial number at the end to preserve compatibility with
> libtraceevent parsing of the parameters."
> PCI/ERR: Introduce PCI_ERS_RESULT_PANIC
> - None
> PCI: Establish common CXL Port protocol error flow
> - get_ras_base(), initialize dport to NULL (Jonathan)
> - Remove guard(device)(&cxlmd->dev) (Jonathan)
> - Fix dev_warns() (Jonathan)
> - Remove comment in cxl_port_error_detected() (Dan)
> - Made pcie_clear_device_status() and pci_aer_clear_fatal_status()
> "CXL" Export namespace (Dan)
> - Update switch-case brackets to follow clang-format (Dan)
> - Add PCI_EXP_TYPE_RC_END for cxl_get_ras_base() (Terry)
> - Add NULL port check in cxl_serial_number() (Terry)
> PCI/CXL: Add RCH support to CXL handlers
> - New commit
> cxl: Update error handlers to support CXL Port devices
> - None
> cxl: Update Endpoint AER uncorrectable handler
> - Update commit message (DaveJ)
> - s/cxl_handle_aer()/cxl_uncor_aer_present()/g (Jonathan)
> - cxl_uncor_aer_present(): Leave original result calculation based on
> if a UCE is present and the provided state (Terry)
> - Add call to pci_print_aer(). AER fails to log because is upstream
> link (Terry)
> cxl: Remove Endpoint AER correctable handler
> - None
> cxl: Enable CXL protocol error reporting
> - None
>
> Changes in v14->v15:
> PCI/AER: Introduce AER-CXL Kfifo in new file, pcie/aer_cxl_vh.c
> - Move pci_dev_get() call to this patch (Dave)
> cxl: Update CXL Endpoint tracing
> - Update commit message
> - Moved cxl_handle_ras/cxl_handle_cor_ras() changes to future patch (Terry)
> PCI/ERR: Introduce PCI_ERS_RESULT_PANIC
> - None
> PCI/AER: Dequeue forwarded CXL error
> - Move pci_dev_get() to cxl_forward_error() (Dave)
> - Move in is_cxl_error() change from later patch (Terry)
> PCI: Establish common CXL Port protocol error flow
> - Update commit message and title. Added Bjorn's ack.
> - Move CE and UCE handling logic here (Terry)
> cxl: Update error handlers to support CXL Port protocol errors
> - New commit (Terry)
> cxl: Update Endpoint AER uncorrectable handler
> - Title update (Terry)
> - Change cxl_pci_error-detected() to handle & log AER (Terry)
> - Update commit message (Terry)
> - Moved cxl_handle_ras()/cxl_handle_cor_ras() to earlier patch (Terry)
> cxl: Remove Endpoint AER correctable handler
> - Remove cxl_pci_cor_error_detected(). Is not needed. AER is logged
> in the AER driver. (Dan)
> - Update commit message
>
> Changes in v13->v14:
> PCI: Move CXL DVSEC definitions into uapi/linux/pci_regs.h
> - Add Jonathan's and Dan's review-by
> - Update commit title prefix (Bjorn)
> - Revert format fix for cxl_sbr_masked() (Jonathan)
> - Update 'Compute Express Link' comment block (Jonathan)
> - Move PCI_DVSEC_CXL_FLEXBUS definitions to later patch where
> used (Jonathan)
> - Removed stray change (Bjorn)
> PCI: Update CXL DVSEC definitions
> - New patch. Split from previous patch such that there is now a separate
> move patch and a format fix patch.
> - Formatting update requested (Bjorn)
> - Remove PCI_DVSEC_HEADER1_LENGTH_MASK because it duplicates
> PCI_DVSEC_HEADER1_LEN() (Bjorn)
> - Add Dan's review-by
> PCI: Introduce pcie_is_cxl()
> - Move FLEXBUS_STATUS DVSEC here (Jonathan)
> - Remove check for EP and USP (Dan)
> - Update commit message (Bjorn)
> - Fix writing past 80 columns (Bjorn)
> - Add pci_is_pcie() parent bridge check at beginning of function (Bjorn)
> PCI: Replace cxl_error_is_native() with pcie_aer_is_native()
> - New commit
> cxl/pci: Move CXL driver's RCH error handling into core/ras_rch.c
> - Add sign-off for Dan and Jonathan
> - Revert inadvertent formatting of cxl_dport_map_rch_aer() (Jonathan)
> - Remove default value for CXL_RCH_RAS (Dan)
> - Remove unnecessary pci.h include in core.h & ras_rch.c (Jonathan)
> - Add linux/types.h include in ras_rch.c (Jonathan)
> - Change CONFIG_CXL_RCH_RAS -> CONFIG_CXL_RAS (Dan)
> PCI/AER: Export pci_aer_unmask_internal_errors
> - New commit. Bjorn requested separating out and adding immediatetly
> before being used. This is called from cxl_rch_enable_rcec() in
> following patch.
> PCI/AER: Update is_internal_error() to be non-static is_aer_internal_error()
> - New commit
> PCI/AER: Move CXL RCH error handling to aer_cxl_rch.c
> - Add review-by and signed-off for Dan
> - Commit message fixup (Dan)
> - Update commit message with use-case description (Dan, Lukas)
> - Make cxl_error_is_native() static (Dan)
> - Make is_internal_error() non-static, non-export (Terry)
> PCI/AER: Use guard() in cxl_rch_handle_error_iter()
> - Add review-by for Jonathan, Dave Jiang, Dan WIlliams, and Bjorn
> - Remove cleanup.h (Jonathan)
> - Reverted comment removal (Bjorn)
> - Move this patch after pci/pcie/aer_cxl_rch.c creation (Bjorn)
> PCI/AER: Replace PCIEAER_CXL symbol with CXL_RAS
> - New commit
> PCI/AER: Report CXL or PCIe bus type in AER trace logging
> - Merged with Dan's commit. Changes are moving bus_type the last
> parameter in function calls (Dan)
> - Removed all DCOs because of changes (Terry)
> - Update commit message (Bjorn)
> - Add Bjorn's ack-by
> PCI/AER: Update struct aer_err_info with kernel-doc formatting
> - New commit
> cxl/mem: Clarify @host for devm_cxl_add_nvdimm()
> - New commit
> cxl/port: Remove "enumerate dports" helpers
> - New commit
> cxl/port: Fix devm resource leaks around with dport management
> - New commit
> cxl/port: Move dport operations to a driver event
> - New commit
> cxl/port: Move dport RAS reporting to a port resource
> - New commit
> cxl: Map CXL Endpoint Port and CXL Switch Port RAS registers
> - Correct message spelling (Terry)
> cxl/port: Move endpoint component register management to cxl_port
> - Correct message spelling (Terry)
> cxl/port: Map Port component registers before switchport init
> - Updates to use cxl_port_setup_regs() (Dan)
> cxl: Change CXL handlers to use guard() instead of scoped_guard()
> - Add reviewed-by for Jonathan and Dave Jiang
> PCI/ERR: Introduce PCI_ERS_RESULT_PANIC
> - Add review-by for Dan
> - Update Title prefix (Bjorn)
> - Removed merge_result. Only logging error for device reporting the
> error (Dan)
> - Remove PCI_ERS_RESULT_PANIC paragraph in pci-error-recovery.rst (Bjorn)
> PCI/AER: Move AER driver's CXL VH handling to pcie/aer_cxl_vh.c
> - Replaced workqueue_types.h include with 'struct work_struct'
> predeclaration (Bjorn)
> - Update error message (Bjorn)
> - Reordered 'struct cxl_proto_err_work_data' (Bjorn)
> - Remove export of cxl_error_is_native() here (Bjorn)
> cxl/port: Unify endpoint and switch port lookup
> - New patch
> PCI/AER: Dequeue forwarded CXL error
> - Update commit title's prefix (Bjorn)
> - Add pdev ref get in AER driver before enqueue and add pdev ref put in
> CXL driver after dequeue and handling (Dan)
> - Removed handling to simplify patch context (Terry)
> PCI: Introduce CXL Port protocol error handlers
> - Add Dave Jiang's review-by
> - Update commit message & headline (Bjorn)
> - Refactor cxl_port_error_detected()/cxl_port_cor_error_detected() to
> one line (Jonathan)
> - Remove cxl_walk_port(). Only log the erroring device. No port walking. (Dan)
> - Remove cxl_pci_drv_bound(). Check for 'is_cxl' parent port is
> sufficient (Dan)
> - Remove device_lock_if()
> - Combine CE and UCE here (Terry)
> cxl: Update Endpoint uncorrectable protocol error handling
> - Update commit headline (Bjorn)
> - Rename pci_error_detected()/pci_cor_error_detected() ->
> cxl_pci_error_detected/cxl_pci_cor_error_detected() (Jonathan)
> - Remove now-invalid comment in cxl_error_detected() (Jonathan)
> - Split into separate patches for UCE and CE (Terry)
> cxl: Update Endpoint correctable protocol error handling
> - New commit
> - Change cxl_cor_error_detected() parameter to &pdev->dev device from
> memdev device. (Terry)
> cxl: Enable CXL protocol errors during CXL Port probe
> - Update commit title's prefix (Bjorn)
> Changes in v12->v13:
> CXL/PCI: Move CXL DVSEC definitions into uapi/linux/pci_regs.h
> - Add Dave Jiang's reviewed-by
> - Remove changes to existing PCI_DVSEC_CXL_PORT* defines. Update commit
> message. (Jonathan)
> PCI/CXL: Introduce pcie_is_cxl()
> - Add Ben's "reviewed-by"
> cxl/pci: Remove unnecessary CXL Endpoint handling helper functions
> - None
> cxl/pci: Remove unnecessary CXL RCH handling helper functions
> - None
> cxl: Remove CXL VH handling in CONFIG_PCIEAER_CXL conditional blocks from core
> - None
> cxl: Move CXL driver's RCH error handling into core/ras_rch.c
> - None
> CXL/AER: Replace device_lock() in cxl_rch_handle_error_iter() with guard() lock
> - New patch
> CXL/AER: Move AER drivers RCH error handling into pcie/aer_cxl_rch.c
> - Add forward declararation of 'struct aer_err_info' in pci/pci.h (Terry)
> - Changed copyright date from 2025 to 2023 (Jonathan)
> - Add David Jiang's, Jonathan's, and Ben's review-by
> - Readd 'struct aer_err_info' (Bot)
> PCI/AER: Report CXL or PCIe bus error type in trace logging
> - Remove duplicated aer_err_info inline comments. Is already in the
> kernel-doc header (Ben)
> cxl/pci: Update RAS handler interfaces to also support CXL Ports
> - None
> cxl/pci: Log message if RAS registers are unmapped
> - Added Bens review-by
> cxl/pci: Unify CXL trace logging for CXL Endpoints and CXL Ports
> - Added Dave Jiang's review-by
> cxl/pci: Update cxl_handle_cor_ras() to return early if no RAS errors
> - Add Ben's review-by
> cxl/pci: Map CXL Endpoint Port and CXL Switch Port RAS registers
> - Change as result of dport delay fix. No longer need switchport and
> endport approach. Refactor. (Terry)
> CXL/PCI: Introduce PCI_ERS_RESULT_PANIC
> - Add Dave Jiang's, Jonathan's, Ben's review-by
> - Typo fix (Ben)
> CXL/AER: Introduce pcie/aer_cxl_vh.c in AER driver for forwarding CXL errors
> - Add Dave Jiang's review-by
> - Update error message (Ben)
> cxl: Introduce cxl_pci_drv_bound() to check for bound driver
> - Add Dave Jiang's review-by.
> cxl: Change CXL handlers to use guard() instead of scoped_guard()
> - New patch
> cxl/pci: Introduce CXL protocol error handlers for endpoints
> - Updated all the implemetnation and commit message. (Terry)
> - Refactored cxl_cor_error_detected()/cxl_error_detected() to remove
> pdev (Dave Jiang)
> CXL/PCI: Introduce CXL Port protocol error handlers
> - Move get_pci_cxl_host_dev() and cxl_handle_proto_error() to Dequeue
> patch (Terry)
> - Remove EP case in cxl_get_ras_base(), not used. (Terry)
> - Remove check for dport->dport_dev (Dave)
> - Remove whitespace (Terry)
> PCI/AER: Dequeue forwarded CXL error
> - Rewrite cxl_handle_proto_error() and cxl_proto_err_work_fn() (Terry)
> - Rename get_cxl_host dev() to be get_cxl_port() (Terry)
> - Remove exporting of unused function, pci_aer_clear_fatal_status() (Dave Jiang)
> - Change pr_err() calls to ratelimited. (Terry)
> - Update commit message. (Terry)
> - Remove namespace qualifier from pcie_clear_device_status()
> export (Dave Jiang)
> - Move locks into cxl_proto_err_work_fn() (Dave)
> - Update log messages in cxl_forward_error() (Ben)
> CXL/PCI: Export and rename merge_result() to pci_ers_merge_result()
> - Renamed pci_ers_merge_result() to pcie_ers_merge_result().
> pci_ers_merge_result() is already used in eeh driver. (Bot)
> CXL/PCI: Introduce CXL uncorrectable protocol error recovery
> - Rewrite report_error_detected() and cxl_walk_port (Terry)
> - Add guard() before calling cxl_pci_drv_bound() (Dave Jiang)
> - Add guard() calls for EP (cxlds->cxlmd->dev & pdev->dev) and ports
> (pdev->dev & parent cxl_port) in cxl_report_error_detected() and
> cxl_handle_proto_error() (Terry)
> - Remove unnecessary check for endpoint port. (Dave Jiang)
> - Remove check for RCIEP EP in cxl_report_error_detected() (Terry)
> CXL/PCI: Enable CXL protocol errors during CXL Port probe
> - Add dev and dev_is_pci() NULL checks in cxl_unmask_proto_interrupts() (Terry)
> - Add Dave Jiang's and Ben's review-by
> CXL/PCI: Disable CXL protocol error interrupts during CXL Port cleanup
> - Added dev and dev_is_pci() checks in cxl_mask_proto_interrupts() (Terry)
>
> Changes in v11 -> v12:
> cxl/pci: Remove unnecessary CXL Endpoint handling helper functions
> - Added Dave Jiang's review by
> - Moved to front of series
> cxl/pci: Remove unnecessary CXL RCH handling helper functions
> - Add reviewed-by for Alejandro & Dave Jiang
> - Moved to front of series
> cxl: Remove ifdef blocks of CONFIG_PCIEAER_CXL from core/pci.c
> - Update CONFIG_CXL_RAS in CXL Kconfig to have CXL_PCI dependency (Terry)
> CXL/AER: Remove CONFIG_PCIEAER_CXL and replace with CONFIG_CXL_RAS
> - Added review-by for Sathyanarayanan
> - Changed Kconfig dependency from PCIEAER_CXL to PCIEAER. Moved
> this backwards into this patch.
> cxl: Move CXL driver RCH error handling into CONFIG_CXL_RCH_RAS conditio
> - Moved CXL_RCH_RAS Kconfig definition here from following commit
> CXL/AER: Introduce aer_cxl_rch.c into AER driver for handling CXL RCH errors
> - Rename drivers/pci/pcie/cxl_rch.c to drivers/pci/pcie/aer_cxl_rch.c (Lukas)
> - Removed forward declararation of 'struct aer_err_info' in pci/pci.h (Terry)
> CXL/PCI: Move CXL DVSEC definitions into uapi/linux/pci_regs.h
> - Change formatting to be same as existing definitions
> - Change GENMASK() -> __GENMASK() and BIT() to _BITUL()
> PCI/CXL: Introduce pcie_is_cxl()
> - Add review-by for Alejandro
> - Add comment in set_pcie_cxl() explaining why updating parent status.
> PCI/AER: Report CXL or PCIe bus error type in trace logging
> - Change aer_err_info::is_cxl to be bool a bitfield. Update structure padding. (Lukas)
> - Add kernel-doc for 'struct aer_err_info' (Lukas)
> cxl/pci: Unify CXL trace logging for CXL Endpoints and CXL Ports
> - Correct parameters to call trace_cxl_aer_correctable_error() (Shiju)
> - Add reviewed-by for Jonathan and Shiju
> cxl/pci: Map CXL Endpoint Port and CXL Switch Port RAS registers
> - Add check for dport_parent->rch before calling cxl_dport_init_ras_reporting().
> - RCH dports are initialized from cxl_dport_init_ras_reporting cxl_mem_probe().
> CXL/PCI: Introduce PCI_ERS_RESULT_PANIC
> - Documentation requested by (Lukas)
> CXL/AER: Introduce aer_cxl_vh.c in AER driver for forwarding CXL errors
> - Rename drivers/pci/pcie/cxl_aer.c to drivers/pci/pcie/aer_cxl_vh.c (Lukas)
> cxl: Introduce cxl_pci_drv_bound() to check for bound driver
> - New patch
> PCI/AER: Dequeue forwarded CXL error
> - Add guard for CE case in cxl_handle_proto_error() (Dave)
> - Updated commit message (Terry)
> CXL/PCI: Introduce CXL Port protocol error handlers
> - Add call to cxl_pci_drv_bound() in cxl_handle_proto_error() and
> pci_to_cxl_dev() (Lukas)
> - Change cxl_error_detected() -> cxl_cor_error_detected() (Terry)
> - Remove NULL variable assignments (Jonathan)
> - Replace bus_find_device() with find_cxl_port_by_uport() for upstream
> port searches. (Dave)
> CXL/PCI: Export and rename merge_result() to pci_ers_merge_result()
> - Remove static inline pci_ers_merge_result() definition for !CONFIG_PCIEAER.
> Is not needed. (Lukas)
> CXL/PCI: Introduce CXL uncorrectable protocol error recovery
> - Clean up port discovery in cxl_do_recovery() (Dave)
> - Add PCI_EXP_TYPE_RC_END to type check in cxl_report_error_detected()
>
> Changes in v10 -> v11:
> cxl: Remove ifdef blocks of CONFIG_PCIEAER_CXL from core/pci.c
> - New patch
> CXL/AER: Remove CONFIG_PCIEAER_CXL and replace with CONFIG_CXL_RAS
> - New patch
> cxl/pci: Remove unnecessary CXL RCH handling helper functions
> - New patch
> cxl: Move CXL driver RCH error handling into CONFIG_CXL_RCH_RAS conditional block
> - New patch
> CXL/AER: Introduce rch_aer.c into AER driver for handling CXL RCH errors
> - Remove changes in code-split and move to earlier, new patch
> - Add #include <linux/bitfield.h> to cxl_ras.c
> - Move cxl_rch_handle_error() & cxl_rch_enable_rcec() declarations from pci.h
> to aer.h, more localized.
> - Introduce CONFIG_CXL_RCH_RAS, includes Makefile changes, ras.c ifdef changes
> CXL/PCI: Move CXL DVSEC definitions into uapi/linux/pci_regs.h
> - New patch
> PCI/CXL: Introduce pcie_is_cxl()
> - Amended set_pcie_cxl() to check for Upstream Port's and EP's parent
> downstream port by calling set_pcie_cxl(). (Dan)
> - Retitle patch: 'Add' -> 'Introduce'
> - Add check for CXL.mem and CXL.cache (Alejandro, Dan)
> PCI/AER: Report CXL or PCIe bus error type in trace logging
> - Remove duplicate call to trace_aer_event() (Shiju)
> - Added Dan William's and Dave Jiang's reviewed-by
> CXL/AER: Update PCI class code check to use FIELD_GET()
> - Add #include <linux/bitfield.h> to cxl_ras.c (Terry)
> - Removed line wrapping at "(CXL 3.2, 8.1.12.1)". (Jonathan)
> cxl/pci: Log message if RAS registers are unmapped
> - Added Dave Jiang's review-by (Terry)
> cxl/pci: Unify CXL trace logging for CXL Endpoints and CXL Ports
> - Updated CE and UCE trace routines to maintian consistent TP_Struct ABI
> and unchanged TP_printk() logging. (Shiju, Alison)
> cxl/pci: Update cxl_handle_cor_ras() to return early if no RAS errors
> - Added Dave Jiang and Jonathan Cameron's review-by
> - Changes moved to core/ras.c
> cxl/pci: Map CXL Endpoint Port and CXL Switch Port RAS registers
> - Use local pointer for readability in cxl_switch_port_init_ras() (Jonathan Cameron)
> - Rename port to be ep in cxl_endpoint_port_init_ras() (Dave Jiang)
> - Rename dport to be parent_dport in cxl_endpoint_port_init_ras()
> and cxl_switch_port_init_ras() (Dave Jiang)
> - Port helper changes were in cxl/port.c, now in core/ras.c (Dave Jiang)
> cxl/pci: Introduce CXL Endpoint protocol error handlers
> - cxl_error_detected() - Change handlers' scoped_guard() to guard() (Jonathan)
> - cxl_error_detected() - Remove extra line (Shiju)
> - Changes moved to core/ras.c (Terry)
> - cxl_error_detected(), remove 'ue' and return with function call. (Jonathan)
> - Remove extra space in documentation for PCI_ERS_RESULT_PANIC definition
> - Move #include "pci.h from cxl.h to core.h (Terry)
> - Remove unnecessary includes of cxl.h and core.h in mem.c (Terry)
> CXL/AER: Introduce cxl_aer.c into AER driver for forwarding CXL errors
> - Move RCH implementation to cxl_rch.c and RCH declarations to pci/pci.h. (Terry)
> - Introduce 'struct cxl_proto_err_kfifo' containing semaphore, fifo,
> and work struct. (Dan)
> - Remove embedded struct from cxl_proto_err_work (Dan)
> - Make 'struct work_struct *cxl_proto_err_work' definition static (Jonathan)
> - Add check for NULL cxl_proto_err_kfifo to determine if CXL driver is
> not registered for workqueue. (Dan)
> PCI/AER: Dequeue forwarded CXL error
> - Reword patch commit message to remove RCiEP details (Jonathan)
> - Add #include <linux/bitfield.h> (Terry)
> - is_cxl_rcd() - Fix short comment message wrap (Jonathan)
> - is_cxl_rcd() - Combine return calls into 1 (Jonathan)
> - cxl_handle_proto_error() - Move comment earlier (Jonathan)
> - Usse FIELD_GET() in discovering class code (Jonathan)
> - Remove BDF from cxl_proto_err_work_data. Use 'struct pci_dev *' (Dan)
> CXL/PCI: Introduce CXL Port protocol error handlers
> - Removed check for PCI_EXP_TYPE_RC_END in cxl_report_error_detected() (Terry)
> - Update is_cxl_error() to check for acceptable PCI EP and port types
> CXL/PCI: Export and rename merge_result() to pci_ers_merge_result()
> - pci_ers_merge_result() - Change export to non-namespace and rename
> to be pci_ers_merge_result() (Jonathan)
> - Move pci_ers_merge_result() definition to pci.h. Needs pci_ers_result (Terry)
> CXL/PCI: Introduce CXL uncorrectable protocol error recovery
> - pci_ers_merge_results() - Move to earlier patch
> CXL/PCI: Disable CXL protocol error interrupts during CXL Port cleanup
> - Remove guard() in cxl_mask_proto_interrupts(). Observed device lockup/block
> during testing. (Terry)
>
> Changes in v9 -> v10:
> - Add drivers/pci/pcie/cxl_aer.c
> - Add drivers/cxl/core/native_ras.c
> - Change cxl_register_prot_err_work()/cxl_unregister_prot_err_work to return void
> - Check for pcie_ports_native in cxl_do_recovery()
> - Remove debug logging in cxl_do_recovery()
> - Update PCI_ERS_RESULT_PANIC definition to indicate is CXL specific
> - Revert trace logging changes: name,parent -> memdev,host.
> - Use FIELD_GET() to check for EP class code (cxl_aer.c & native_ras.c).
> - Change _prot_ to _proto_ everywhere
> - cxl_rch_handle_error_iter(), check if driver is cxl_pci_driver
> - Remove cxl_create_prot_error_info(). Move logic into forward_cxl_error()
> - Remove sbdf_to_pci() and move logic into cxl_handle_proto_error()
> - Simplify/refactor get_pci_cxl_host_dev()
> - Simplify/refactor cxl_get_ras_base()
> - Move patch 'Remove unnecessary CXL Endpoint handling helper functions' to front
> - Update description for 'CXL/PCI: Introduce CXL Port protocol error
> handlers' with why state is not used to determine handling
> - Introduce cxl_pci_drv_bound() and call from cxl_rch_handle_error_iter()
> Changes in v8 -> v9:
> - Updated reference counting to use pci_get_device()/pci_put_device() in
> cxl_disable_prot_errors()/cxl_enable_prot_errors
> - Refactored cxl_create_prot_err_info() to fix reference counting
> - Removed 'struct cxl_port' driver changes for error handler. Instead
> check for CXL device type (EP or Port device) and call handler
> - Make pcie_is_cxl() static inline in include/linux/linux.h
> - Remove NULL check in create_prot_err_info()
> - Change success return in cxl_ras_init() to use hardcoded 0
> - Changed 'struct work_struct cxl_prot_err_work' declaration to static
> - Change to use rate limited log with dev anchor in forward_cxl_error()
> - Refactored forward-cxl_error() to remove severity auto variable
> - Changed pci_aer_clear_nonfatal_status() to be static inline for
> !(CONFIG_PCIEAER)
> - Renamed merge_result() to be cxl_merge_result()
> - Removed 'ue' condition in cxl_error_detected()
> - Updated 2nd parameter in call to __cxl_handle_cor_ras()/__cxl_handle_ras()
> in unify patch
> - Added log message for failure while assigning interrupt disable callback
> - Updated pci_aer_mask_internal_errors() to use pci_clear_and_set_config_dword()
> - Simplified patch titles for clarity
> - Moved CXL error interrupt disabling into cxl/core/port.c with CXL Port
> teardown
> - Updated 'struct cxl_port_err_info' to only contain sbdf and severity
> Removed everything else.
> - Added pdev and CXL device get_device()/put_device() before calling handlers
>
> Changes in v7 -> v8:
> [Dan] Use kfifo. Move handling to CXL driver. AER forwards error to CXL
> driver
> [Dan] Add device reference incrementors where needed throughout
> [Dan] Initiate CXL Port RAS init from Switch Port and Endpoint Port init
> [Dan] Combine CXL Port and CXL Endpoint trace routine
> [Dan] Introduce aer_info::is_cxl. Use to indicate CXL or PCI errors
> [Jonathan] Add serial number for all devices in trace
> [DaveJ] Move find_cxl_port() change into patch using it
> [Terry] Move CXL Port RAS init into cxl/port.c
> [Terry] Moved kfifo functions into cxl/core/ras.c
>
> Changes in v6 -> v7:
> [Terry] Move updated trace routine call to later patch. Was causing build
> error.
>
> Changes in v5 -> v6:
> [Ira] Move pcie_is_cxl(dev) define to a inline function
> [Ira] Update returning value from pcie_is_cxl_port() to bool w/o cast
> [Ira] Change cxl_report_error_detected() cleanup to return correct bool
> [Ira] Introduce and use PCI_ERS_RESULT_PANIC
> [Ira] Reuse comment for PCIe and CXL recovery paths
> [Jonathan] Add type check in for cxl_handle_cor_ras() and cxl_handle_ras()
> [Jonathan] cxl_uport/dport_init_ras_reporting(), added a mutex.
> [Jonathan] Add logging example to patches updating trace output
> [Jonathan] Make parameter 'const' to eliminate for cast in match_uport()
> [Jonathan] Use __free() in cxl_pci_port_ras()
> [Terry] Add patch to log the PCIe SBDF along with CXL device name
> [Terry] Add patch to handle CXL endpoint and RCH DP errors as CXL errors
> [Terry] Remove patch w USP UCE fatal support @ aer_get_device_error_info()
> [Terry] Rebase to cxl/next commit 5585e342e8d3 ("cxl/memdev: Remove unused partition values")
> [Gregory] Pre-initialize pointer to NULL in cxl_pci_port_ras()
> [Gregory] Move AER driver bus name detection to a static function
>
> Changes in v4 -> v5:
> [Alejandro] Refactor cxl_walk_bridge to simplify 'status' variable usage
> [Alejandro] Add WARN_ONCE() in __cxl_handle_ras() and cxl_handle_cor_ras()
> [Ming] Remove unnecessary NULL check in cxl_pci_port_ras()
> [Terry] Add failure check for call to to_cxl_port() in cxl_pci_port_ras()
> [Ming] Use port->dev for call to devm_add_action_or_reset() in
> cxl_dport_init_ras_reporting() and cxl_uport_init_ras_reporting()
> [Jonathan] Use get_device()/put_device() to prevent race condition in
> cxl_clear_port_error_handlers() and cxl_clear_port_error_handlers()
> [Terry] Commit message cleanup. Capitalize keywords from CXL and PCI
> specifications
>
> Changes in v3 -> v4:
> [Lukas] Capitalize PCIe and CXL device names as in specifications
> [Lukas] Move call to pcie_is_cxl() into cxl_port_devsec()
> [Lukas] Correct namespace spelling
> [Lukas] Removed export from pcie_is_cxl_port()
> [Lukas] Simplify 'if' blocks in cxl_handle_error()
> [Lukas] Change panic message to remove redundant 'panic' text
> [Ming] Update to call cxl_dport_init_ras_reporting() in RCH case
> [lkp@intel] 'host' parameter is already removed. Remove parameter description too.
> [Terry] Added field description for cxl_err_handlers in pci.h comment block
>
> Changes in v1 -> v2:
> [Jonathan] Remove extra NULL check and cleanup in cxl_pci_port_ras()
> [Jonathan] Update description to DSP map patch description
> [Jonathan] Update cxl_pci_port_ras() to check for NULL port
> [Jonathan] Dont call handler before handler port changes are present (patch order)
> [Bjorn] Fix linebreak in cover sheet URL
> [Bjorn] Remove timestamps from test logs in cover sheet
> [Bjorn] Retitle AER commits to use "PCI/AER:"
> [Bjorn] Retitle patch#3 to use renaming instead of refactoring
> [Bjorn] Fix base commit-id on cover sheet
> [Bjorn] Add VH spec reference/citation
> [Terry] Removed last 2 patches to enable internal errors. Is not needed
> because internal errors are enabled in AER driver.
> [Dan] Create cxl_do_recovery() and pci_driver::cxl_err_handlers.
> [Dan] Use kernel panic in CXL recovery
> [Dan] cxl_port_hndlrs -> cxl_port_error_handlers
>
> Dan Williams (4):
> cxl: Tighten CPER kfifo registration API and symbol visibility
> cxl: Rename find_cxl_port() to find_cxl_port_by_dport()
> cxl/pci: Thread port and dport through RAS handling helpers
> cxl: Add port and dport identifiers to CXL AER trace events
>
> Terry Bowman (9):
> cxl/ras: Fix cxl_rch_get_aer_severity() wrong severity register
> acpi/apei/ghes: Use raw_spinlock_t for CXL CPER work locks
> PCI/AER: Introduce AER-CXL protocol error kfifo
> PCI: Establish common CXL Port protocol error flow
> PCI/CXL: Add RCH support to CXL handlers
> cxl: Update CXL Endpoint AER handler
> PCI: Cache PCI DSN into pci_dev->dsn during probe
> PCI/CXL: Mask/Unmask CXL protocol errors
> Documentation: cxl: Document CXL protocol error handling
>
> Documentation/driver-api/cxl/index.rst | 1 +
> .../cxl/linux/protocol-error-handling.rst | 222 ++++++++++
> MAINTAINERS | 2 +
> drivers/acpi/apei/ghes.c | 66 +--
> drivers/cxl/core/core.h | 36 +-
> drivers/cxl/core/port.c | 28 +-
> drivers/cxl/core/ras.c | 395 ++++++++++++------
> drivers/cxl/core/ras_rch.c | 20 +-
> drivers/cxl/core/trace.c | 35 ++
> drivers/cxl/core/trace.h | 91 ++--
> drivers/cxl/cxlmem.h | 7 +
> drivers/cxl/cxlpci.h | 11 +-
> drivers/cxl/pci.c | 16 +-
> drivers/pci/pci.h | 1 -
> drivers/pci/pcie/Makefile | 1 +
> drivers/pci/pcie/aer.c | 45 +-
> drivers/pci/pcie/aer_cxl_rch.c | 39 +-
> drivers/pci/pcie/aer_cxl_vh.c | 235 +++++++++++
> drivers/pci/pcie/portdrv.h | 10 +-
> drivers/pci/probe.c | 14 +
> include/cxl/event.h | 17 +-
> include/linux/aer.h | 26 ++
> include/linux/pci.h | 1 +
> tools/testing/cxl/Kbuild | 1 +
> tools/testing/cxl/test/mock.c | 12 +
> 25 files changed, 1013 insertions(+), 319 deletions(-)
> create mode 100644 Documentation/driver-api/cxl/linux/protocol-error-handling.rst
> create mode 100644 drivers/pci/pcie/aer_cxl_vh.c
>
>
> base-commit: a13c140cc289c0b7b3770bce5b3ad42ab35074aa
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH] x86/mce: Document the mce=print_all command line parameter
From: Randy Dunlap @ 2026-07-23 3:27 UTC (permalink / raw)
To: Shreshth Srivastava, Dave Hansen, Borislav Petkov,
Jonathan Corbet, x86
Cc: Shuah Khan, Andrew Morton, Mike Rapoport, Dapeng Mi, Marco Elver,
Ethan Nelson-Moore, Jakub Kicinski, Eric Biggers, Li RongQing,
linux-doc, linux-kernel, Thomas Gleixner, Ingo Molnar,
H . Peter Anvin, Sohil Mehta, Tony Luck
In-Reply-To: <20260723013730.3033-1-shreshth.srivastava@intel.com>
On 7/22/26 6:37 PM, Shreshth Srivastava wrote:
> The mce=print_all command line option is supported but has never
> been documented. Add it.
>
> Suggested-by: Sohil Mehta <sohil.mehta@intel.com>
> Signed-off-by: Shreshth Srivastava <shreshth.srivastava@intel.com>
> Acked-by: Tony Luck <tony.luck@intel.com>
Looks good. Thanks.
Acked-by: Randy Dunlap <rdunlap@infradead.org>
> ---
> Documentation/admin-guide/kernel-parameters.txt | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index eb4e0d28f74b..0ec18a5597a2 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -3792,6 +3792,9 @@ Kernel parameters
> nobootlog
> disable boot machine check logging.
>
> + print_all
> + print all machine check logs to the console.
> +
> monarchtimeout (number)
> sets the time in us to wait for other CPUs on machine
> checks. 0 to disable.
--
~Randy
^ permalink raw reply
* Re: [PATCH v5 14/36] mm/damon: skip private node memory in DAMON migration and pageout
From: Gregory Price @ 2026-07-23 3:25 UTC (permalink / raw)
To: SJ Park
Cc: arun.george, balbirs, brendan.jackman, yuzenghui, apopple,
alucerop, matthew.brost, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, corbet, skhan, gregkh, rafael, dakr, djbw,
vishal.l.verma, dave.jiang, alison.schofield, osandov, jannh,
pfalcato, jackmanb, hannes, ziy, pbonzini, osalvador,
joshua.hahnjy, rakie.kim, byungchul, ying.huang, kasong, qi.zheng,
shakeel.butt, baohua, axelrasmussen, yuanchu, weixugc, yury.norov,
linux, longman, ridong.chen, tj, mkoutny, jgg, jhubbard, peterx,
baolin.wang, npache, ryan.roberts, dev.jain, lance.yang,
usama.arif, xu.xin16, chengming.zhou, roman.gushchin, muchun.song,
linux-kernel, linux-doc, driver-core, nvdimm, linux-cxl,
linux-debuggers, linux-fsdevel, kvm, cgroups, damon,
linux-kselftest, kernel-team
In-Reply-To: <20260723001943.95854-1-sj@kernel.org>
On Wed, Jul 22, 2026 at 05:19:42PM -0700, SJ Park wrote:
> On Wed, 22 Jul 2026 08:16:56 -0400 Gregory Price <gourry@gourry.net> wrote:
>
> > > "by default". Does that mean it could be reclaimable in some situations? If
> > > so, could we check if it is reclaimable?
> > >
> >
> > See the damon changes in:
> >
> > https://lore.kernel.org/linux-mm/al-pkvmgIxGu3LzM@gourry-fedora-PF4VCD3F/T/#mfb99303c85dd9d4c3f58832dc28fc55583a7217a
>
> Summarizing what you want to say, quoting something from the patch, or at least
> calling it "26th patch of this series" would have made reviewing much easier.
> Cc-ing damon@ for only patches that toucing DAMON source files and the cover
> letter of the series could also be helpful. Please consider doing some of
> these for future replies.
>
I have been very much discouraged from this kind of trimming because it
removes the context of the series from the individual patches - making it
even more confusing.
But I understand, I will try to remember to do this for you.
> So, I understand later patches will make it optionally reclaimable and update
> this restriction by the 26th patch? That sounds fair. But, could we drop "by
> default" from the above comment for reducing the confusion?
Yes, but you bring up a good point - I think this two-step process is
actually poor form and that I need to redo it.
Maybe this can be redone as node states (features?) such as:
N_MEMORY_RECLAIMABLE
N_MEMORY_DEMOTABLE
N_MEMORY_TIERABLE
so these damon changes will forego this confusing intermediate step of:
if (folio_is_private_node(folio))
and go straight to
if (node_is_reclaimable(folio_nid(folio)))
In the first patch - without ever having to revisit damon again.
> >
> > Technically there is nothing in migration core to prevent migration
> > operations, it's done on a service basis - hotunplug, reclaim/demotion,
> > user numa (mbind, migrate/move_pages) etc.
> >
> > Operations on private nodes/private node folioes are refused if the
> > capability bit is not set.
>
> I haven't had a chance to read the entire series, sorry about that. So, do you
> mean the above blocking is not really needed, or that will conditionally be
> allowed by another later patch?
>
I will reduce this all to a single patch in v6, I see where things can
be improved now based on a few pieces of feedback.
Thanks SJ!
~Gregory
^ permalink raw reply
* Re: [PATCH v2 2/2] mm/zswap: Support batch writeback in shrink_memcg()
From: Johannes Weiner @ 2026-07-23 2:27 UTC (permalink / raw)
To: Hao Jia
Cc: akpm, tj, shakeel.butt, mhocko, yosry, mkoutny, nphamcs,
chengming.zhou, muchun.song, roman.gushchin, linux-mm,
linux-kernel, linux-doc, Hao Jia
In-Reply-To: <20260717085151.22822-3-jiahao.kernel@gmail.com>
On Fri, Jul 17, 2026 at 04:51:51PM +0800, Hao Jia wrote:
> @@ -1369,7 +1402,7 @@ static void shrink_worker(struct work_struct *w)
> goto resched;
> }
>
> - ret = shrink_memcg(memcg);
> + ret = shrink_memcg(memcg, NR_ZSWAP_WB_BATCH);
> /* drop the extra reference */
> mem_cgroup_put(memcg);
>
> @@ -1493,7 +1526,7 @@ bool zswap_store(struct folio *folio)
> objcg = get_obj_cgroup_from_folio(folio);
> if (objcg && !obj_cgroup_may_zswap(objcg)) {
> memcg = get_mem_cgroup_from_objcg(objcg);
> - if (shrink_memcg(memcg)) {
> + if (shrink_memcg(memcg, 1)) {
Why 64 for the global limit but only 1 for the cgroup limit? That
seems arbitrary in multiple ways.
Direct reclaim, kswapd, proactive reclaim, cgroup limit reclaim use
SWAP_CLUSTER_MAX for the batch size near-universally. It's magic too
to be sure, but at least you wouldn't have to make up new magic?
Otherwise, the patch looks good to me.
^ permalink raw reply
* Re: [PATCH v2 1/2] mm/zswap: Fix global shrinker when memory cgroup is disabled
From: Johannes Weiner @ 2026-07-23 2:13 UTC (permalink / raw)
To: Hao Jia
Cc: akpm, tj, shakeel.butt, mhocko, yosry, mkoutny, nphamcs,
chengming.zhou, muchun.song, roman.gushchin, linux-mm,
linux-kernel, linux-doc, Hao Jia, stable
In-Reply-To: <20260717085151.22822-2-jiahao.kernel@gmail.com>
On Fri, Jul 17, 2026 at 04:51:50PM +0800, Hao Jia wrote:
> From: Hao Jia <jiahao1@lixiang.com>
>
> Zswap writeback on hitting the pool limit is broken when memory cgroup
> is disabled, because mem_cgroup_iter() always returns NULL. Therefore,
> the global shrinker shrink_worker() always takes the !memcg branch.
> After MAX_RECLAIM_RETRIES empty walks, the worker simply gives up, so it
> fails to write back anything.
>
> Therefore, when memory cgroup is disabled, fall through with the !memcg
> branch and shrink the root memcg directly.
>
> With memcg disabled, shrink_memcg() only returns -ENOENT when the root
> LRU is empty, which means the total pages are already below thr. In the
> absence of heavy concurrent zswap stores, the loop then safely bails out
> via the zswap_total_pages() <= thr check; otherwise, it will resume
> shrinking the memcg after processing the reschedule check. For any other
> return value from shrink_memcg(), the loop is guaranteed to terminate,
> either after MAX_RECLAIM_RETRIES failures or once the threshold is met.
>
> Fixes: a65b0e7607cc ("zswap: make shrinking memcg-aware")
> Cc: stable@vger.kernel.org
> Suggested-by: Nhat Pham <nphamcs@gmail.com>
> Acked-by: Nhat Pham <nphamcs@gmail.com>
> Acked-by: Yosry Ahmed <yosry@kernel.org>
> Reported-by: Yosry Ahmed <yosry@kernel.org>
> Closes: https://lore.kernel.org/all/CAO9r8zPVzMKFbCixxD-qgtRrkFxWVrHiZZeLc=eyTPKPVQgX4g@mail.gmail.com
> Signed-off-by: Hao Jia <jiahao1@lixiang.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
^ permalink raw reply
* Re: [PATCH] x86/mce: Document the mce=print_all command line parameter
From: Sohil Mehta @ 2026-07-23 1:41 UTC (permalink / raw)
To: Shreshth Srivastava, Dave Hansen, Borislav Petkov,
Jonathan Corbet, x86
Cc: Shuah Khan, Randy Dunlap, Andrew Morton, Mike Rapoport, Dapeng Mi,
Marco Elver, Ethan Nelson-Moore, Jakub Kicinski, Eric Biggers,
Li RongQing, linux-doc, linux-kernel, Thomas Gleixner,
Ingo Molnar, H . Peter Anvin, Tony Luck
In-Reply-To: <20260723013730.3033-1-shreshth.srivastava@intel.com>
On 7/22/2026 6:37 PM, Shreshth Srivastava wrote:
> The mce=print_all command line option is supported but has never
> been documented. Add it.
>
> Suggested-by: Sohil Mehta <sohil.mehta@intel.com>
> Signed-off-by: Shreshth Srivastava <shreshth.srivastava@intel.com>
> Acked-by: Tony Luck <tony.luck@intel.com>
> ---
> Documentation/admin-guide/kernel-parameters.txt | 3 +++
> 1 file changed, 3 insertions(+)
>
Reviewed-by: Sohil Mehta <sohil.mehta@intel.com>
^ permalink raw reply
* [PATCH] x86/mce: Document the mce=print_all command line parameter
From: Shreshth Srivastava @ 2026-07-23 1:37 UTC (permalink / raw)
To: Dave Hansen, Borislav Petkov, Jonathan Corbet, x86
Cc: Shuah Khan, Randy Dunlap, Andrew Morton, Mike Rapoport, Dapeng Mi,
Marco Elver, Ethan Nelson-Moore, Jakub Kicinski, Eric Biggers,
Li RongQing, linux-doc, linux-kernel, Thomas Gleixner,
Ingo Molnar, H . Peter Anvin, Shreshth Srivastava, Sohil Mehta,
Tony Luck
The mce=print_all command line option is supported but has never
been documented. Add it.
Suggested-by: Sohil Mehta <sohil.mehta@intel.com>
Signed-off-by: Shreshth Srivastava <shreshth.srivastava@intel.com>
Acked-by: Tony Luck <tony.luck@intel.com>
---
Documentation/admin-guide/kernel-parameters.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index eb4e0d28f74b..0ec18a5597a2 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3792,6 +3792,9 @@ Kernel parameters
nobootlog
disable boot machine check logging.
+ print_all
+ print all machine check logs to the console.
+
monarchtimeout (number)
sets the time in us to wait for other CPUs on machine
checks. 0 to disable.
--
2.53.0
^ permalink raw reply related
* Re: Re: [PATCH net-next v11 2/5] hinic3: Add ethtool statistic ops
From: Fan Gong @ 2026-07-23 1:35 UTC (permalink / raw)
To: dimitri.daskalakis1
Cc: andrew+netdev, davem, edumazet, gongfan1, guoxin09, horms,
hramamurthy, ioana.ciornei, kuba, linux-doc, linux-kernel,
luosifu, maxime.chevallier, mohsin.bashr, netdev, pabeni,
shijing34, tengpeisen, wudi234, wulike1, zhengjiezhen,
zhoushuai28, august.hewei
In-Reply-To: <190879f9-2447-449d-acf8-9f9e26651b24@gmail.com>
> > +
> > +static u64 get_val_of_ptr(u32 size, const void *ptr)
> > +{
> > + u64 ret = size == sizeof(u64) ? *(u64 *)ptr :
> > + size == sizeof(u32) ? *(u32 *)ptr :
> > + size == sizeof(u16) ? *(u16 *)ptr :
> > + *(u8 *)ptr;
> > +
> > + return ret;
> > +}
> Why is this needed? It looks like all the counters in
> hinic3_rx_queue_stats, hinic3_tx_queue_stats, and hinic3_port_stats are
> u64.
Thanks for the review. We will remove it in the next patchset.
^ permalink raw reply
* Re: Re: [PATCH net-next v11 1/5] hinic3: Add ethtool queue ops
From: Fan Gong @ 2026-07-23 1:32 UTC (permalink / raw)
To: mohsin.bashr
Cc: andrew+netdev, davem, dimitri.daskalakis1, edumazet, gongfan1,
guoxin09, horms, hramamurthy, ioana.ciornei, kuba, linux-doc,
linux-kernel, luosifu, maxime.chevallier, netdev, pabeni,
shijing34, tengpeisen, wudi234, wulike1, zhengjiezhen,
zhoushuai28, august.hewei
In-Reply-To: <c4473a79-567a-430b-b463-f45d4ef3ea28@gmail.com>
> > Implement following ethtool callback function:
> > .get_ringparam
> > .set_ringparam
> >
> > These callbacks allow users to utilize ethtool for detailed
> > queue depth configuration and monitoring.
> >
> > Change port_state_mutex to state_lock as a unified mutex lock
> > in hinic3_nic_dev.
> >
> > Tightens the success criterion for hinic3_rx_fill_buffers() in
> > hinic3_configure_rxqs().
> >
> > Changes hinic3_tx_csum()/hinic3_tx_offload() to propagate
> > skb_checksum_help() failures into a TX drop.
> >
> > Renames hinic3_uninit_qps() to hinic3_get_cur_qps().
> >
> > Splits hinic3_open_channel() into hinic3_prepare_channel() and
> > hinic3_open_channel().
> >
>
> It may just be me, but I feel like there is a lot going in this single
> patch. Ideally, a patch should carry one logical change. This patch is
> doing 3 things: refactor, new code, bug-fixing in the hinic3_close() path.
Thanks for the following 3 review comments. We are pushing new codes to the
net-next. However, fixing review comments leads to some refactoring and
bug-fixing codes.
We are considering split patch #1 into smaller ones in the next patchset.
> > @@ -330,6 +330,8 @@ static void hinic3_link_status_change(struct net_device *netdev,
> > netif_carrier_off(netdev);
> > netdev_dbg(netdev, "Link is down\n");
> > }
> > +
> > + return;
>
> Unrelated?
Sorry for our negligence. We will remove this unrelated change in the next
patchset.
> > @@ -493,10 +580,15 @@ static int hinic3_close(struct net_device *netdev)
> > return 0;
> > }
> >
> > + mutex_lock(&nic_dev->state_lock);
> > hinic3_vport_down(netdev);
> > hinic3_close_channel(netdev);
> > - hinic3_uninit_qps(nic_dev, &qp_params);
> > - hinic3_free_channel_resources(netdev, &qp_params, &nic_dev->q_params);
> > + hinic3_get_cur_qps(nic_dev, &qp_params);
> > + hinic3_free_channel_resources(netdev, &qp_params,
> > + &nic_dev->q_params);
> > + hinic3_free_nicio_res(nic_dev);
> > + hinic3_destroy_num_qps(netdev);
>
> free_nicio_res(), and destroy_num_qps() looks like fixes to me on the
> clean-up path. Should these go to the net tree?
As mentioned above, we reckon it as a necessary fix to commit new codes, thus
we put it in this patch.
^ permalink raw reply
* Re: [PATCH v2 0/2] mm/zswap: Fixes and improves the zswap global shrinker
From: Hao Jia @ 2026-07-23 1:21 UTC (permalink / raw)
To: Andrew Morton, Yosry Ahmed, nphamcs
Cc: tj, hannes, shakeel.butt, mhocko, mkoutny, chengming.zhou,
muchun.song, roman.gushchin, linux-mm, linux-kernel, linux-doc,
Hao Jia
In-Reply-To: <7dbde343-fbbf-6a1d-1785-ebf9f5a04f6b@gmail.com>
On 2026/7/20 09:26, Hao Jia wrote:
>
>
> On 2026/7/18 12:40, Andrew Morton wrote:
>> On Fri, 17 Jul 2026 18:28:04 -0700 Yosry Ahmed <yosry@kernel.org> wrote:
>>
>>>>
>>>> The [1/2] changelog lacks a description of how the flaw impacts users.
>>>> Please describe this fully and maintain that info within the
>>>> changelogging. This info helps -stable maintainers and others
>>>> understand why we're proposing a backport and helps myself and others
>>>> with timing decisions.
>>>
>>> The first line in the changelog should be sufficient imo: "Zswap
>>> writeback on hitting the pool limit is broken when memory cgroup is
>>> disabled"
>>
>> "broken"? Perhaps this means "fails to occur".
>>
>> But what is the userspace-visible impact? IOW, why are we proposing a
>> backport?
>>
>
> Perhaps the first paragraph of the commit1 message could be modified as
> follows? I have added a description of the issues that occur without
> this patch.
>
Hi Andrew, Yosry, and Nhat,
Any thoughts on this change?
Thanks,
Hao
> Zswap writeback on hitting the pool limit fails to occur when memory
> cgroup is disabled, because mem_cgroup_iter() always returns NULL.
> Therefore, the global shrinker shrink_worker() always takes the !memcg
> branch. After MAX_RECLAIM_RETRIES empty walks, the worker simply gives
> up, so it fails to write back anything. As a result, once the pool
> reaches the zswap limit, every subsequent zswap shrink work run is a
> no-op. This leads to zswap store failures, forcing pages to bypass zswap
> and be written directly to the backing swap device, which can trigger
> issues such as LRU inversion.
>
>
> Thanks,
> Hao
^ permalink raw reply
* Re: [PATCH V11 0/9] famfs: port into fuse
From: John Groves @ 2026-07-23 0:32 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Amir Goldstein, John Groves, Dan Williams, Bernd Schubert,
Alison Schofield, John Groves, Jonathan Corbet, Shuah Khan,
Vishal Verma, Dave Jiang, Matthew Wilcox, Jan Kara,
Alexander Viro, David Hildenbrand, Christian Brauner,
Darrick J . Wong, Randy Dunlap, Jeff Layton, Jonathan Cameron,
Stefan Hajnoczi, Joanne Koong, Josef Bacik, Bagas Sanjaya,
Chen Linxuan, James Morse, Fuad Tabba, Sean Christopherson,
Shivank Garg, Ackerley Tng, Gregory Price, Andrew Morton,
Namjae Jeon, Lorenzo Stoakes, Aravind Ramesh, Ajay Joshi,
venkataravis@micron.com, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev,
linux-cxl@vger.kernel.org, linux-fsdevel@vger.kernel.org,
fuse-devel@lists.linux.dev
In-Reply-To: <CAJfpegt9PwHksYhsQZeCsW0eooXVVihyieTpeHMmC-+Pq8BWaw@mail.gmail.com>
On 26/07/22 12:46PM, Miklos Szeredi wrote:
> On Mon, 20 Jul 2026 at 12:07, Amir Goldstein <amir73il@gmail.com> wrote:
>
> > It's not about this ioctl, it's about infrastructure reuse.
> > It's about the fact that there is NOTHING special about dax_devlist;
> > and no reason not to manage it via backing_files_map.
> >
> > The reason you stated was that you want userspace to determine the
> > daxdev_index/backing_id - fine, no problem - this is why I asked that
> > you pass it the desired backing_id in the ioctl.
> > In fact I think that all fuse backing_ids would be better assigned by user space
> > so the extension of padding => backing_id is not unique to famfs.
>
> Right, so I suggest to just reuse FUSE_DEV_IOC_BACKING_OPEN and add
> two flags: one to enable user supplied backing ID and the other to
> indicate that fd refers to DAX device. The changes to do compared to
> the current patchset are quite minimal.
>
> > And the best part is that this request requires no changes to your userspace.
> > You already bite the bullet and maintain the new/old uapi but changing the
> > kernel implementation does not require this burden from you, so to be honest
> > I really don't understand why you did not follow Miklos' suggestion.
>
> Code reuse is also important, but I understand John's position to want
> to separate famfs from existing codebase as much as possible. And
> because this is an implementation detail I don't want to make this a
> show stopper. We can easily fix it later.
>
> > > - Lose the famfs interleaved extent format. I have complied with this request.
> > > It makes some things worse, but we'll discuss that later.
> > >
> >
> > I am really not aware of a request to lose the interleaved extent format.
> > How can this patch [2] from Miklos which reimplements this format as a generic
> > uapi be mistaken for a request to lose this format?
>
> Let's not care. We can easily add this later if famfs (or any other
> use case) decides to want this after all.
>
> Making the initial patchset smaller is win in any case.
>
> > Let me recap the former review requests as I understand them:
> >
> > 1. Use ioctl instead of GET_DAXDEV
>
> This is done, just need to reuse the existing ioctl instead of add a
> new one with the same semantics (okay not true because of the fixed
> backing ID extension...)
>
> > 2. Use backing files infra for daxdev table
>
> Leave this later.
>
> > 3. Use ioctl instead of GET_FMAP
>
> Not sure we agreed to use an ioctl for extent mapping. What we did
> agree on was to use a generic interface that doesn't have anything
> famfs specific in it.
>
> John, can you please also look at review comments provided by Sashiko?
>
> Thanks,
> Miklos
This a quick acknowledgment that I appreciate your reply Miklos. I'll respond
to individual points as soon as I can without rushing.
Re: Sashiko - of course! The series has been through /kreview in Chris'
review-prompts repo. Weird that I didn't get any email directly from Sashiko
this time, but I know how to get to it online.
I will make this high-level comment: After reading Amir's initial reply, I
concluded that the requested changes push famfs past the breaking point,
and therefore the only way to keep famfs suitable for its intended usage is
to pivot back to standalone. I do think that may be best all-around for fuse,
famfs and Linux. I don't see any way famfs makes fuse better - it's a very
specialized pattern that doesn't fit in a passthrough-filesystem or
block-filesystem shaped hole. And I don't think it can comply with the
"nothing famfs-specific in GET_FMAP" objective.
If famfs is standalone, it can only ever affect customers who use it, and it
cannot be used as a general purpose file system. When famfs is in fuse, it's
mashed up with code for hundreds or thousands of other use cases. That's not
a win from a risk management standpoint, and it doesn't make famfs better -
certainly not in any way that would mitigate adding a lot of state /
complexity / baggage to famfs.
In good faith,
John
^ permalink raw reply
* Re: [PATCH v5 14/36] mm/damon: skip private node memory in DAMON migration and pageout
From: SJ Park @ 2026-07-23 0:19 UTC (permalink / raw)
To: Gregory Price
Cc: SJ Park, arun.george, balbirs, brendan.jackman, yuzenghui,
apopple, alucerop, matthew.brost, akpm, david, ljs, liam, vbabka,
rppt, surenb, mhocko, corbet, skhan, gregkh, rafael, dakr, djbw,
vishal.l.verma, dave.jiang, alison.schofield, osandov, jannh,
pfalcato, jackmanb, hannes, ziy, pbonzini, osalvador,
joshua.hahnjy, rakie.kim, byungchul, ying.huang, kasong, qi.zheng,
shakeel.butt, baohua, axelrasmussen, yuanchu, weixugc, yury.norov,
linux, longman, ridong.chen, tj, mkoutny, jgg, jhubbard, peterx,
baolin.wang, npache, ryan.roberts, dev.jain, lance.yang,
usama.arif, xu.xin16, chengming.zhou, roman.gushchin, muchun.song,
linux-kernel, linux-doc, driver-core, nvdimm, linux-cxl,
linux-debuggers, linux-fsdevel, kvm, cgroups, damon,
linux-kselftest, kernel-team
In-Reply-To: <amCzmkdOr6AFv_kQ@gourry-fedora-PF4VCD3F>
On Wed, 22 Jul 2026 08:16:56 -0400 Gregory Price <gourry@gourry.net> wrote:
> On Tue, Jul 21, 2026 at 04:46:43PM -0700, SJ Park wrote:
> > On Mon, 20 Jul 2026 15:34:08 -0400 Gregory Price <gourry@gourry.net> wrote:
> >
> > > DAMON operates on physical address ranges, which can cover private
> > > node memory. Skip private-node folios in both DAMON's migration
> > > and reclaim paths.
> > >
> > > Signed-off-by: Gregory Price <gourry@gourry.net>
> > > ---
> > > mm/damon/paddr.c | 9 +++++++++
> > > 1 file changed, 9 insertions(+)
> > >
> > > diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
> > > index e4f98d67461f5..c741a94319750 100644
> > > --- a/mm/damon/paddr.c
> > > +++ b/mm/damon/paddr.c
> > > @@ -12,6 +12,7 @@
> > > #include <linux/swap.h>
> > > #include <linux/memory-tiers.h>
> > > #include <linux/mm_inline.h>
> > > +#include <linux/node_private.h>
> > >
> > > #include "../internal.h"
> > > #include "ops-common.h"
> > > @@ -250,6 +251,10 @@ static unsigned long damon_pa_pageout(struct damon_region *r,
> > > continue;
> > > }
> > >
> > > + /* private node memory is not reclaimable by default */
> > > + if (folio_is_private_node(folio))
> > > + goto put_folio;
> > > +
> >
> > "by default". Does that mean it could be reclaimable in some situations? If
> > so, could we check if it is reclaimable?
> >
>
> See the damon changes in:
>
> https://lore.kernel.org/linux-mm/al-pkvmgIxGu3LzM@gourry-fedora-PF4VCD3F/T/#mfb99303c85dd9d4c3f58832dc28fc55583a7217a
Summarizing what you want to say, quoting something from the patch, or at least
calling it "26th patch of this series" would have made reviewing much easier.
Cc-ing damon@ for only patches that toucing DAMON source files and the cover
letter of the series could also be helpful. Please consider doing some of
these for future replies.
So, I understand later patches will make it optionally reclaimable and update
this restriction by the 26th patch? That sounds fair. But, could we drop "by
default" from the above comment for reducing the confusion?
>
> > Also, what happens if we just try paging out the private node memory? Will it
> > simply fail? Or, make some problems?
> >
>
> user controls for MADV_ commands that affect private node folios would
> skip those folios - exactly same as ZONE_DEVICE folios.
>
> Which I'm not sure damon actually handles correctly presently (zone
> device). I remember looking around damon and thinking to myself "what
> if this is a zone device page?" and not being able to find a satisfying
> answer.
>
> > > if (damos_pa_filter_out(s, folio))
> > > goto put_folio;
> > > else
> > > @@ -344,6 +349,10 @@ static unsigned long damon_pa_migrate(struct damon_region *r,
> > > else
> > > *sz_filter_passed += folio_size(folio) / addr_unit;
> > >
> > > + /* private nodes do not support migration by default */
> > > + if (folio_is_private_node(folio))
> > > + goto put_folio;
> > > +
> >
> > Same questions.
> >
>
> Technically there is nothing in migration core to prevent migration
> operations, it's done on a service basis - hotunplug, reclaim/demotion,
> user numa (mbind, migrate/move_pages) etc.
>
> Operations on private nodes/private node folioes are refused if the
> capability bit is not set.
I haven't had a chance to read the entire series, sorry about that. So, do you
mean the above blocking is not really needed, or that will conditionally be
allowed by another later patch?
Thanks,
SJ
[...]
^ permalink raw reply
* Re: [PATCH v2] mm/page_reporting: Add page_reporting_delay_ms sysctl
From: SJ Park @ 2026-07-23 0:06 UTC (permalink / raw)
To: pratmal
Cc: SJ Park, Anshuman Khandual, David Hildenbrand, Andrew Morton,
Vlastimil Babka, Greg Thelen, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, Zi Yan, linux-mm, linux-kernel,
David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Mike Rapoport, Jonathan Corbet, Shuah Khan, linux-doc
In-Reply-To: <20260722211517.1898228-1-pratmal@google.com>
get_maintainer.pl suggests adding below to the recipients list of this patch.
Let me add them. I show you already Cc-ed David, but with his old email
address.
- David Hildenbrand <david@kernel.org>
- Lorenzo Stoakes <ljs@kernel.org>
- "Liam R. Howlett" <liam@infradead.org>
- Mike Rapoport <rppt@kernel.org>
- Jonathan Corbet <corbet@lwn.net>
- Shuah Khan <skhan@linuxfoundation.org>
- linux-doc@vger.kernel.org
On Wed, 22 Jul 2026 21:15:17 +0000 pratmal@google.com wrote:
> From: Pratyush Mallick <pratmal@google.com>
>
> Currently, the free page reporting daemon uses a hardcoded delay of
> (2 HZ) between reporting intervals. While this is a reasonable
> default, it lacks the flexibility to adapt to varying guest workloads.
>
> A low delay allows aggressive memory reclamation, returning unused
> pages to the host as quickly as possible. However, during spiky
> allocation/free churn, this immediate reporting can lead to a severe
> performance penalty (nested page faults) as the guest re-allocates memory
> that the host has just unmapped. In these scenarios, there is benefit
> from increasing the delay to batch free pages over a longer window,
> absorbing the churn without hypercall and re-fault overhead.
>
> This patch refactors the delay into a dynamically tunable sysctl,
> /proc/sys/vm/page_reporting_delay_ms, measured in milliseconds. The value
> defaults to 2000ms to precisely match the original (2 HZ) behavior.
>
> Signed-off-by: Pratyush Mallick <pratmal@google.com>
> ---
> v2:
> - Documented page_reporting_delay_ms in Documentation/admin-guide/sysctl/vm.rst.
> - v1: https://lore.kernel.org/linux-mm/20260722192935.1646848-1-pratmal@google.com/T/#u
> v1: Fixed feedback from RFC.
> - Added lower and upper cap to sysctl value.
> - Reverted the reordering on page_reporting_delay_ms.
> - Dropped the mod_delayed_work() change.
> - RFC: https://lore.kernel.org/linux-mm/20260714171456.2350037-1-pratmal@google.com/T/#u
> Documentation/admin-guide/sysctl/vm.rst | 13 +++++++++++
> mm/page_reporting.c | 30 ++++++++++++++++++++++---
> 2 files changed, 40 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/admin-guide/sysctl/vm.rst b/Documentation/admin-guide/sysctl/vm.rst
> index b9b0c218bfb4..6efa460b4547 100644
> --- a/Documentation/admin-guide/sysctl/vm.rst
> +++ b/Documentation/admin-guide/sysctl/vm.rst
> @@ -66,6 +66,7 @@ Currently, these files are in /proc/sys/vm:
> - overcommit_ratio
> - page-cluster
> - page_lock_unfairness
> +- page_reporting_delay
> - panic_on_oom
> - percpu_pagelist_high_fraction
> - stat_interval
> @@ -896,6 +897,18 @@ stolen from under a waiter. After the lock is stolen the number of times
> specified in this file (default is 5), the "fair lock handoff" semantics
> will apply, and the waiter will only be awakened if the lock can be taken.
>
> +page_reporting_delay
> +=======================
Why don't you make the lengths of the line and the section title same?
> +
> +This value determines the delay in milliseconds between free page
> +reporting intervals. A lower delay allows aggressive memory
> +reclamation by returning unused pages to the host quickly, while a
> +higher delay helps to batch free pages over a longer window, absorbing
> +allocation/free churn without hypercall and re-fault overhead.
> +
> +The default value is 2000 (2 seconds). The minimum allowed value is
> +0 (immediate reporting) and the maximum allowed value is 10000 (10 seconds).
So it is milliseconds. Why don't you add the unit to the name, like
page_reporting_delay_ms?
Also, coule you elaborate why there is 10 seconds maximum limit? What's the
problem of having no limit, and why 10 seconds is the reasonable one?
> +
> panic_on_oom
> ============
>
> diff --git a/mm/page_reporting.c b/mm/page_reporting.c
> index 942e84b6908a..805da4bc1101 100644
> --- a/mm/page_reporting.c
> +++ b/mm/page_reporting.c
> @@ -6,6 +6,7 @@
> #include <linux/export.h>
> #include <linux/module.h>
> #include <linux/delay.h>
> +#include <linux/sysctl.h>
> #include <linux/scatterlist.h>
Too trivial nit, but, why don't you put the sysctl.h at the end of the list?
>
> #include "page_reporting.h"
> @@ -47,7 +48,10 @@ MODULE_PARM_DESC(page_reporting_order, "Set page reporting order");
> */
> EXPORT_SYMBOL_GPL(page_reporting_order);
>
> -#define PAGE_REPORTING_DELAY (2 * HZ)
> +#define PAGE_REPORTING_DELAY_MS_MAX (10 * MSEC_PER_SEC)
> +
> +static unsigned int page_reporting_delay_ms = 2 * MSEC_PER_SEC;
> +static unsigned int page_reporting_delay_ms_max = PAGE_REPORTING_DELAY_MS_MAX;
> static struct page_reporting_dev_info __rcu *pr_dev_info __read_mostly;
>
> enum {
> @@ -56,6 +60,19 @@ enum {
> PAGE_REPORTING_ACTIVE
> };
>
> +
> +static struct ctl_table page_reporting_sysctls[] = {
> + {
> + .procname = "page_reporting_delay",
> + .data = &page_reporting_delay_ms,
> + .maxlen = sizeof(unsigned int),
> + .mode = 0644,
> + .proc_handler = proc_douintvec_minmax,
> + .extra1 = SYSCTL_ZERO,
> + .extra2 = &page_reporting_delay_ms_max,
> + },
> +};
> +
> /* request page reporting */
> static void
> __page_reporting_request(struct page_reporting_dev_info *prdev)
> @@ -80,7 +97,7 @@ __page_reporting_request(struct page_reporting_dev_info *prdev)
> * now we are limiting this to running no more than once every
> * couple of seconds.
> */
> - schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
> + schedule_delayed_work(&prdev->work, msecs_to_jiffies(page_reporting_delay_ms));
> }
A trivial comment again. Why don't you wrap the above line for 80 columns
limit? I know that's not a hard limit anymore and I show a few lines of this
file already exceeds 80 columns. But seems most lines of this file is still
keeping the 80 columns limit?
>
> /* notify prdev of free page reporting request */
> @@ -340,7 +357,7 @@ static void page_reporting_process(struct work_struct *work)
> */
> state = atomic_cmpxchg(&prdev->state, state, PAGE_REPORTING_IDLE);
> if (state == PAGE_REPORTING_REQUESTED)
> - schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
> + schedule_delayed_work(&prdev->work, msecs_to_jiffies(page_reporting_delay_ms));
Ditto.
> }
>
> static DEFINE_MUTEX(page_reporting_mutex);
> @@ -416,3 +433,10 @@ void page_reporting_unregister(struct page_reporting_dev_info *prdev)
> mutex_unlock(&page_reporting_mutex);
> }
> EXPORT_SYMBOL_GPL(page_reporting_unregister);
> +
> +static int __init page_reporting_sysctl_init(void)
> +{
> + register_sysctl_init("vm", page_reporting_sysctls);
> + return 0;
> +}
> +late_initcall(page_reporting_sysctl_init);
> --
> 2.55.0.229.g6434b31f56-goog
Thanks,
SJ
^ permalink raw reply
* Re: [RFC PATCH v2 00/13] mm/kwatch: dynamic hardware watchpoints for hunting memory corruption
From: Masami Hiramatsu @ 2026-07-23 0:05 UTC (permalink / raw)
To: Jinchao Wang
Cc: Borislav Petkov, Dave Hansen, Andrew Morton, Peter Zijlstra,
Thomas Gleixner, Steven Rostedt, Ingo Molnar, Dave Hansen,
H . Peter Anvin, x86, Arnaldo Carvalho de Melo, Namhyung Kim,
Mark Rutland, Mathieu Desnoyers, David Hildenbrand,
Jonathan Corbet, Matthew Wilcox, Alan Stern, Randy Dunlap,
Alexander Potapenko, Marco Elver, Mike Rapoport, linux-kernel,
linux-mm, linux-trace-kernel, linux-perf-users, linux-doc
In-Reply-To: <0565e7d4-fb0f-4b84-a20c-0c73ac964b51@gmail.com>
On Tue, 21 Jul 2026 08:36:01 -0400
Jinchao Wang <wangjinchao600@gmail.com> wrote:
> On 7/20/2026 10:24 AM, Masami Hiramatsu (Google) wrote:
> > On Fri, 17 Jul 2026 11:10:04 -0700
> > Borislav Petkov <bp@alien8.de> wrote:
>
> >> Looks to me like folks need to sit down and agree on strategy first.
> >
> > Yeah, I need to talk with Jinchao.
> >
> > Like kprobe, wprobe itself integrates watchpoint functionality into
> > the tracing subsystem. Jinchao, I would like to ask you to redesign
> > the kwatch tool based on fprobe and wprobe events in user space?
> > I think that could reduce redundant efforts on the similar feature.
> >
> > Using fprobe entry and exit event trigger, we can make a Function-
> > scoped watch window. Also, since fprobe and wprobe are having BTF
> > typecast support, you can access to the members of data structures
> > more naturally.
> >
> > If you are interested, I would be open to having you handle the
> > development of the necessary features for wprobe.
> >
> > Thank you,
> >
> Thank you, Boris and Masami.
>
> Boris is right that we should agree on the overall strategy before
> moving either series forward, especially since they share the low-level
> HWBP work. Masami is also right that fprobe and wprobe provide a more
> appropriate foundation than introducing KWatch as another standalone
> debugging tool.
Agreed. I just sent v10 because v9 lacked base-commit and Sashiko did
not reviewed. (so it just add a trigger-side BTF support which was
dropped in v9).
Think of v10 as the complete set of ideas we wanted to implement
with this wprobe.
>
> After reconsidering the design, I will stop the standalone KWatch
> series. Instead, I would like to work with Masami on extending wprobe
> to support the function-scoped dynamic-watchpoint use cases that
> motivated KWatch.
Thanks Jinchao, I appreciate to work with you on this project.
>
> Masami, thank you for being open to having me develop the necessary
> wprobe features. I am interested in taking that direction.
OK, please share your thoughts on watchpoint usage in the kernel.
Basically, I will fix the points raised in the review, but please
let me know if you have any other suggestions.
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [PATCH 0/3] tools/mm/page_owner_sort: fix --sort, add module filter, improve usage
From: Andrew Morton @ 2026-07-23 0:01 UTC (permalink / raw)
To: Ye Liu
Cc: Ye Liu, David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, linux-mm, linux-doc, linux-kernel
In-Reply-To: <20260722023726.600200-1-ye.liu@linux.dev>
On Wed, 22 Jul 2026 10:37:23 +0800 Ye Liu <ye.liu@linux.dev> wrote:
> This series improves the page_owner_sort tool with a bug fix, a new
> module-name feature, and better usage text.
>
> Patch 1 fixes a long-standing bug where --sort was silently ignored
> when used without a short option (-a, -m, -p, etc.). The COMP_NO_FLAG
> case fell through to COMP_NUM and overwrote the sort conditions
> configured by parse_sort_args().
>
> Patch 2 adds kernel module name support for sort, cull, and filter
> operations. Page owner stack traces already contain module names in
> the [module] format produced by %pS, but page_owner_sort had no way
> to use them. Records without module frames are assigned "vmlinux".
>
> # Aggregate page usage per module
> ./page_owner_sort input.txt output.txt --cull=mod
>
> # Filter to records from xfs module only
> ./page_owner_sort input.txt output.txt --module xfs
>
> # Sort by module name, then by pid descending
> ./page_owner_sort input.txt output.txt --sort=mod,-pid
>
> Patch 3 lists all available sort keys with abbreviations and examples
> directly in the --sort help section so users no longer need to read
> the source to discover valid keys.
Thanks, these sound like nice changes.
AI review mentioned a couple of things, at least one of which appears legit:
https://sashiko.dev/#/patchset/20260722023726.600200-1-ye.liu@linux.dev
^ permalink raw reply
* [PATCH v4 16/16] WIP: Reproducer for out_put_pages subpool reserve leakage
From: Ackerley Tng via B4 Relay @ 2026-07-22 23:41 UTC (permalink / raw)
To: Muchun Song, Oscar Salvador, David Hildenbrand, Joshua Hahn,
Shakeel Butt, Nhat Pham, Andrew Morton, Peter Xu, Wupeng Ma, fvdl,
rientjes, jthoughton, Mike Kravetz, Johannes Weiner, Michal Hocko,
Roman Gushchin, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Hongxiang Lou,
Miaohe Lin
Cc: vannapurve, erdemaktas, linux-mm, linux-kernel, cgroups,
linux-doc, Ackerley Tng
In-Reply-To: <20260722-hugetlb-alloc-failure-fixes-v4-0-88e8b81970dc@google.com>
From: Ackerley Tng <ackerleytng@google.com>
Add a highly precise C reproducer and accompanying bash execution script
to exercise, validate, and stress-test the `out_put_pages` error path
rollback semantics in `hugetlb_reserve_pages()`.
How it works:
1. The bash script sets the system-wide HugeTLB pool to a highly constrained
baseline of exactly `nr_hugepages = 1` and `nr_overcommit_hugepages = 0`.
2. It mounts a `hugetlbfs` instance with `-o pagesize=2M,min_size=2M,size=4M`,
which causes the kernel to immediately consume the 1 available global page
as the subpool's mount-time minimum size reserve (`rsv_hugepages` becomes 1).
3. The C reproducer then attempts a shared `mmap()` for `4M` (2 pages).
- `hugepage_subpool_get_pages()` requests 2 pages, sees 1 reserved, and
requests 1 additional global page.
- `hugetlb_acct_memory()` attempts to secure that global page but immediately
fails with `-ENOMEM` because the pool is exhausted.
- The kernel jumps to the `out_put_pages` error path, calling
`hugepage_subpool_put_pages()` to symmetrically roll back the reservation.
4. The script verifies that the subpool successfully retains its 1reserved page
during the failure and returns it cleanly to the global pool upon unmount,
proving that no underflow, double-free, or reserve leakage occurs in the
`out_put_pages` boundary path.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
hugetlb_reserve_pages_out_put_pages.c | 49 +++++++++++
hugetlb_reserve_pages_out_put_pages.sh | 153 +++++++++++++++++++++++++++++++++
2 files changed, 202 insertions(+)
diff --git a/hugetlb_reserve_pages_out_put_pages.c b/hugetlb_reserve_pages_out_put_pages.c
new file mode 100644
index 0000000000000..9e63fc8997d57
--- /dev/null
+++ b/hugetlb_reserve_pages_out_put_pages.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+
+int main(int argc, char **argv)
+{
+ const char *file_path;
+ size_t size;
+ int fd;
+ void *addr;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s <hugetlbfs_file> <size_in_bytes>\n",
+ argv[0]);
+ return 1;
+ }
+
+ file_path = argv[1];
+ size = strtoull(argv[2], NULL, 0);
+
+ fd = open(file_path, O_CREAT | O_RDWR, 0666);
+ if (fd < 0)
+ err(1, "open");
+
+ printf("Attempting to mmap %zu bytes shared on %s...\n", size,
+ file_path);
+ addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ if (errno == ENOMEM) {
+ printf("mmap failed with ENOMEM as expected.\n");
+ close(fd);
+ return 0;
+ }
+ perror("mmap failed with unexpected error");
+ close(fd);
+ return 1;
+ }
+
+ printf("ERROR: mmap SUCCEEDED unexpectedly at %p\n", addr);
+ munmap(addr, size);
+ close(fd);
+ return 1;
+}
diff --git a/hugetlb_reserve_pages_out_put_pages.sh b/hugetlb_reserve_pages_out_put_pages.sh
new file mode 100755
index 0000000000000..030e1915539b4
--- /dev/null
+++ b/hugetlb_reserve_pages_out_put_pages.sh
@@ -0,0 +1,153 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+set -e
+
+if [ "$EUID" -ne 0 ]; then
+ echo "Please run as root"
+ exit 1
+fi
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$SCRIPT_DIR"
+
+# Detect default hugepage size to support both 2MB and 1GB pages robustly
+hpz=$(grep -i hugepagesize /proc/meminfo | awk '{print $2}')
+kb=$hpz
+mb=$((kb / 1024))
+hpage_size_bytes=$((kb * 1024))
+
+hpage_dir="hugepages-${kb}kB"
+SYSFS_PATH="/sys/kernel/mm/hugepages/$hpage_dir"
+
+MNT_PATH="/tmp/mnt_hugetlb_repro"
+FILE_PATH="$MNT_PATH/test_file"
+
+# Save original values for safe restoration
+orig_nr=$(cat "$SYSFS_PATH/nr_hugepages")
+orig_overcommit=$(cat "$SYSFS_PATH/nr_overcommit_hugepages")
+
+cleanup() {
+ echo "Cleaning up..."
+ rm -f "$FILE_PATH"
+ umount "$MNT_PATH" 2>/dev/null
+ rmdir "$MNT_PATH" 2>/dev/null
+ echo "$orig_nr" > "$SYSFS_PATH/nr_hugepages"
+ echo "$orig_overcommit" > "$SYSFS_PATH/nr_overcommit_hugepages"
+ echo "Cleanup done."
+}
+trap cleanup EXIT
+
+# Verify reproducer binary exists
+if [ ! -x ./hugetlb_reserve_pages_out_put_pages ]; then
+ echo "reproducer binary './hugetlb_reserve_pages_out_put_pages' not found or not executable."
+ echo "Please compile it first: gcc -static -o hugetlb_reserve_pages_out_put_pages hugetlb_reserve_pages_out_put_pages.c"
+ exit 1
+fi
+
+# 1. Set global pool such that only the mount-time reservation can succeed
+echo 1 > "$SYSFS_PATH/nr_hugepages"
+echo 0 > "$SYSFS_PATH/nr_overcommit_hugepages"
+
+initial_resv=$(cat "$SYSFS_PATH/resv_hugepages")
+echo "Initial resv_hugepages (before mount): $initial_resv"
+
+# 2. Mount with min_size = 1 page, max size = 2 pages
+min_size_str="${mb}M"
+max_size_str="$((mb * 2))M"
+mmap_size_bytes=$((hpage_size_bytes * 2))
+
+mkdir -p "$MNT_PATH"
+echo "Mounting hugetlbfs with pagesize=${mb}M, min_size=$min_size_str, size=$max_size_str..."
+if ! mount -t hugetlbfs -o "pagesize=${mb}M,min_size=$min_size_str,size=$max_size_str" none "$MNT_PATH"; then
+ echo "Failed to mount hugetlbfs"
+ exit 1
+fi
+
+resv_after_mount=$(cat "$SYSFS_PATH/resv_hugepages")
+echo "resv_hugepages after mount: $resv_after_mount"
+expected_after_mount=$((initial_resv + 1))
+if [ "$resv_after_mount" != "$expected_after_mount" ]; then
+ echo "ERROR: resv_hugepages is not $expected_after_mount after mount (actual: $resv_after_mount)!"
+ exit 1
+fi
+
+# Check mount stats after mount
+expected_bsize=$hpage_size_bytes
+bsize_S=$(stat -f -c "%S" "$MNT_PATH")
+bsize_s=$(stat -f -c "%s" "$MNT_PATH")
+echo "Mount block size after mount: $bsize_S / $bsize_s (expected: $expected_bsize)"
+if [ "$bsize_S" != "$expected_bsize" ] && [ "$bsize_s" != "$expected_bsize" ]; then
+ echo "ERROR: Unexpected mount block size after mount (actual S:$bsize_S s:$bsize_s, expected: $expected_bsize)"
+ exit 1
+fi
+
+actual_stats_mount=$(stat -f -c "%b %f %a" "$MNT_PATH")
+expected_stats_mount="2 2 2"
+echo "Mount stats after mount (total free avail): $actual_stats_mount (expected: $expected_stats_mount)"
+if [ "$actual_stats_mount" != "$expected_stats_mount" ]; then
+ echo "ERROR: Unexpected mount stats after mount: $actual_stats_mount (expected: $expected_stats_mount)"
+ exit 1
+fi
+
+# 3. Run the reproducer to trigger the out_put_pages failure path
+echo "Running reproducer (expecting mmap failure with ENOMEM)..."
+if ./hugetlb_reserve_pages_out_put_pages "$FILE_PATH" "$mmap_size_bytes"; then
+ echo "Reproducer finished successfully."
+ resv_after_mmap=$(cat "$SYSFS_PATH/resv_hugepages")
+ echo "resv_hugepages after failed mmap: $resv_after_mmap"
+ expected_after_mmap=$expected_after_mount
+ if [ "$resv_after_mmap" = "$expected_after_mmap" ]; then
+ echo "RESULT: out_put_pages EXERCISED (resv_hugepages preserved at $expected_after_mmap as expected)"
+
+ # Check mount stats
+ expected_bsize=$hpage_size_bytes
+ bsize_S=$(stat -f -c "%S" "$MNT_PATH")
+ bsize_s=$(stat -f -c "%s" "$MNT_PATH")
+ echo "Mount block size: $bsize_S / $bsize_s (expected: $expected_bsize)"
+ if [ "$bsize_S" != "$expected_bsize" ] && [ "$bsize_s" != "$expected_bsize" ]; then
+ echo "ERROR: Unexpected mount block size (actual S:$bsize_S s:$bsize_s, expected: $expected_bsize)"
+ exit 1
+ fi
+
+ actual_stats=$(stat -f -c "%b %f %a" "$MNT_PATH")
+ expected_stats="2 2 2"
+ echo "Mount stats (total free avail): $actual_stats (expected: $expected_stats)"
+ if [ "$actual_stats" != "$expected_stats" ]; then
+ echo "RESULT: Unexpected mount stats after failed mmap (FAIL)"
+ exit 1
+ else
+ echo "RESULT: Mount stats restored to $expected_stats as expected (PASS)"
+ fi
+ else
+ echo "RESULT: Unexpected resv_hugepages value: $resv_after_mmap (expected: $expected_after_mmap)"
+ exit 1
+ fi
+else
+ echo "FAIL: Reproducer returned non-zero (mmap didn't fail with ENOMEM)"
+ exit 1
+fi
+
+# 4. Disable trap and do manual cleanup to check for final unmount underflow
+trap - EXIT
+
+echo "Unmounting..."
+umount "$MNT_PATH"
+rmdir "$MNT_PATH"
+
+final_resv=$(cat "$SYSFS_PATH/resv_hugepages")
+echo "Final resv_hugepages (after unmount): $final_resv"
+
+# Restore original values
+echo "Restoring original hugepage settings..."
+echo "$orig_nr" > "$SYSFS_PATH/nr_hugepages"
+echo "$orig_overcommit" > "$SYSFS_PATH/nr_overcommit_hugepages"
+
+if [ "$final_resv" = "$initial_resv" ]; then
+ echo "RESULT: State restored to $initial_resv (or cleaned up if fixed)"
+ echo "ALL DONE."
+ exit 0
+else
+ echo "RESULT: Underflow/Leak/Incorrect state detected! (final_resv = $final_resv, expected = $initial_resv)"
+ echo "ALL DONE."
+ exit 1
+fi
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related
* [PATCH v4 15/16] WIP: Reproducer for false restoration on shared HugeTLB mappings
From: Ackerley Tng via B4 Relay @ 2026-07-22 23:41 UTC (permalink / raw)
To: Muchun Song, Oscar Salvador, David Hildenbrand, Joshua Hahn,
Shakeel Butt, Nhat Pham, Andrew Morton, Peter Xu, Wupeng Ma, fvdl,
rientjes, jthoughton, Mike Kravetz, Johannes Weiner, Michal Hocko,
Roman Gushchin, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Hongxiang Lou,
Miaohe Lin
Cc: vannapurve, erdemaktas, linux-mm, linux-kernel, cgroups,
linux-doc, Ackerley Tng
In-Reply-To: <20260722-hugetlb-alloc-failure-fixes-v4-0-88e8b81970dc@google.com>
From: Ackerley Tng <ackerleytng@google.com>
(This reproducer was hacked up and not meant to be merged.)
hugetlb_unreserve_pages() unconditionally returns reservations to the subpool
(via hugepage_subpool_put_pages()). This means that regardless of whether a
subpool reservation was actually used, the reservation is processed by the
subpool structure.
To create a false restoration, the reproducer performs these steps:
1. Mount with min_size=2M (1 page). Global resv_hugepages becomes 1.
2. The program maps 4MB (2 pages) shared (which also grows the file to
4MB). Global resv_hugepages becomes 2 (1 from the mount, 1 new global
reservation).
3. The program populates only the first page. Global resv_hugepages decrements
to 1 (reservation consumed by allocation).
4. The program exits (closing VMAs/fds). For shared mappings, reservations are
associated with the file inode, so they remain active. Global resv_hugepages
remains 1.
5. The script truncates the file to 2MB (truncate -s 2M).
+ This synchronously triggers hugetlb_unreserve_pages() to release the
reservation of the truncated range (the unallocated 2nd page).
+ It calls hugepage_subpool_put_pages(spool, 1).
+ On Vanilla Kernel (Buggy):
+ used_hpages is 0 (not tracked).
+ used_hpages (0) < min_hpages (1) is TRUE.
+ The subpool incorrectly restores the reservation (spool->rsv_hpages
becomes 1), even though Page 0 is still allocated and satisfies the
mount's minimum guarantee.
+ hugepage_subpool_put_pages() returns 0, skipping
hugetlb_acct_memory(h, -1).
+ Result: Global resv_hugepages remains stuck at 1 (Leak).
+ On Fixed Kernel:
+ used_hpages is tracked and is initially 2.
+ hugepage_subpool_put_pages(1) decrements used_hpages to 1.
+ used_hpages (1) < min_hpages (1) is FALSE.
+ The subpool does not restore the reservation.
+ hugepage_subpool_put_pages() returns 1.
+ hugetlb_acct_memory(h, -1) is called.
+ Result: Global resv_hugepages decrements to 0 (No leak).
When the filesystem is unmounted, hugetlbfs_put_super drops the subpool
reference. Since the filesystem is being unmounted, the reference count drops to
0, triggering unlock_or_release_subpool.
Inside unlock_or_release_subpool, the kernel checks if the subpool is free using
subpool_is_free.
+ On the buggy kernel, subpool_is_free checks if spool->rsv_hpages is equal to
spool->min_hpages. Because of the phantom reservation, spool->rsv_hpages was
restored to 1. Since min_hpages is 1, the check (1 == 1) returns true.
+ Since the subpool is considered free, the kernel releases the initial
mount-time reservation by calling hugetlb_acct_memory to decrement
resv_huge_pages by spool->min_hpages (which is 1).
+ This decrement reduces resv_huge_pages from 1 (the leaked state) to 0.
As a result, the leaked reservation is cleaned up during unmount and does not
persist afterward.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
subpool_shared_leak.c | 43 +++++++++++++++++++++++++
subpool_shared_leak.sh | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 130 insertions(+)
diff --git a/subpool_shared_leak.c b/subpool_shared_leak.c
new file mode 100644
index 0000000000000..80b972af1c5ce
--- /dev/null
+++ b/subpool_shared_leak.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define HPAGE_SIZE (2 * 1024 * 1024)
+
+int main(int argc, char **argv)
+{
+ const char *file_path;
+ void *addr;
+ int fd;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <file_path>\n", argv[0]);
+ return 1;
+ }
+ file_path = argv[1];
+
+ fd = open(file_path, O_CREAT | O_RDWR, 0666);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ addr = mmap(NULL, 2 * HPAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ perror("mmap");
+ close(fd);
+ return 1;
+ }
+
+ /* Allocate 1st page only. 2nd page remains unallocated (but reserved). */
+ *(char *)addr = 1;
+
+ munmap(addr, 2 * HPAGE_SIZE);
+ close(fd);
+
+ return 0;
+}
diff --git a/subpool_shared_leak.sh b/subpool_shared_leak.sh
new file mode 100755
index 0000000000000..619036145c523
--- /dev/null
+++ b/subpool_shared_leak.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+if [ "$EUID" -ne 0 ]; then
+ echo "Please run as root"
+ exit 1
+fi
+
+MNT_PATH="/tmp/mnt_hugetlb_shared_leak"
+FILE_PATH="$MNT_PATH/test_file"
+
+# Save original values
+orig_nr=$(cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages)
+
+cleanup() {
+ echo "Cleaning up..."
+ rm -f "$FILE_PATH"
+ umount "$MNT_PATH" 2>/dev/null
+ rmdir "$MNT_PATH" 2>/dev/null
+ echo "$orig_nr" > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
+ echo "Cleanup done."
+}
+trap cleanup EXIT
+
+# 1. Set nr_hugepages to 2
+echo 2 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
+
+# 2. Mount hugetlbfs with min_size=2M (1 page)
+mkdir -p "$MNT_PATH"
+if ! mount -t hugetlbfs -o min_size=2M none "$MNT_PATH"; then
+ echo "Failed to mount hugetlbfs"
+ exit 1
+fi
+
+# Check resv_hugepages after mount (should be 1)
+initial_resv=$(cat /sys/kernel/mm/hugepages/hugepages-2048kB/resv_hugepages)
+echo "Initial resv_hugepages (after mount): $initial_resv"
+if [ "$initial_resv" -ne 1 ]; then
+ echo "ERROR: Initial resv_hugepages is not 1!"
+ exit 1
+fi
+
+# Verify reproducer binary exists
+if [ ! -x ./subpool_shared_leak ]; then
+ echo "reproducer binary './subpool_shared_leak' not found or not executable."
+ echo "Please compile it first: gcc -static -o subpool_shared_leak subpool_shared_leak.c"
+ exit 1
+fi
+
+# 3. Run helper to map 4MB, allocate 2MB, and close.
+# This creates 2 reservations, consumes 1 (by allocating Page 0).
+# The unallocated Page 1 reservation remains active in the inode's resv_map.
+echo "Running helper..."
+./subpool_shared_leak "$FILE_PATH"
+
+resv_after_helper=$(cat /sys/kernel/mm/hugepages/hugepages-2048kB/resv_hugepages)
+echo "resv_hugepages after helper (should be 1): $resv_after_helper"
+# Page 0 is allocated (no longer reserved). Page 1 is reserved.
+# So resv_hugepages should be 1.
+if [ "$resv_after_helper" -ne 1 ]; then
+ echo "ERROR: resv_hugepages is not 1 after helper run!"
+ exit 1
+fi
+
+# 4. Truncate file to 2MB (releases Page 1 reservation)
+echo "Truncating file to 2MB (releasing 1 page reservation)..."
+truncate -s 2M "$FILE_PATH"
+
+# Check resv_hugepages after truncate.
+# Since Page 0 is still allocated (and in page cache), and satisfies the
+# min_size=2M guarantee, we should have 0 reservations remaining.
+# If the bug is present, the truncate path will incorrectly restore the
+# reservation to the subpool and skip releasing it globally, leaving
+# resv_hugepages at 1.
+final_resv=$(cat /sys/kernel/mm/hugepages/hugepages-2048kB/resv_hugepages)
+echo "Final resv_hugepages (after 2MB truncate): $final_resv"
+
+if [ "$final_resv" -eq 1 ]; then
+ echo "RESULT: LEAK DETECTED (FAIL)"
+ exit 1
+elif [ "$final_resv" -eq 0 ]; then
+ echo "RESULT: NO LEAK (PASS)"
+ exit 0
+else
+ echo "RESULT: UNEXPECTED STATE ($final_resv)"
+ exit 2
+fi
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related
* [PATCH v4 14/16] WIP: Reproducer for subpool usage leak
From: Ackerley Tng via B4 Relay @ 2026-07-22 23:41 UTC (permalink / raw)
To: Muchun Song, Oscar Salvador, David Hildenbrand, Joshua Hahn,
Shakeel Butt, Nhat Pham, Andrew Morton, Peter Xu, Wupeng Ma, fvdl,
rientjes, jthoughton, Mike Kravetz, Johannes Weiner, Michal Hocko,
Roman Gushchin, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Hongxiang Lou,
Miaohe Lin
Cc: vannapurve, erdemaktas, linux-mm, linux-kernel, cgroups,
linux-doc, Ackerley Tng
In-Reply-To: <20260722-hugetlb-alloc-failure-fixes-v4-0-88e8b81970dc@google.com>
From: Ackerley Tng <ackerleytng@google.com>
(This reproducer was hacked up and not meant to be merged.)
The kernel leaks subpool usage and the subpool structure itself if a HugeTLBfs
mount specifying size (which sets max_hpages on the subpool) is created.
subpool_leak_max_size.sh reproduces this with the following steps:
1. Create mount, specifying size=2M (1 page). This sets max_hpages = 1 on the
subpool, but does not reserve any pages.
2. Set nr_hugepages = 0 and nr_overcommit_hugepages = 0 so that physical
allocations will fail.
3. Run fallocate -l 2M on a file in the mount.
+ This calls hugetlbfs_fallocate, which attempts to allocate a page by
calling alloc_hugetlb_folio.
+ alloc_hugetlb_folio calls hugepage_subpool_get_pages to track the
allocation against the subpool limit. This increments used_hpages to 1.
+ Physical allocation fails because nr_hugepages is 0.
+ Before patch (Buggy):
+ The error path in alloc_hugetlb_folio sees gbl_chg is 1 (indicating
we tried to allocate a global page) and incorrectly skips calling
hugepage_subpool_put_pages.
+ fallocate fails and returns to userspace, but the subpool used_hpages
counter remains leaked at 1.
+ After patch:
+ The error path always calls hugepage_subpool_put_pages if map_chg is
true, restoring used_hpages to 0.
4. Unmount the filesystem.
+ During unmount, the kernel calls unlock_or_release_subpool to clean up
the subpool.
+ It checks if the subpool is free using subpool_is_free, which returns
whether used_hpages is 0.
+ Before patch (Buggy):
+ Since used_hpages leaked and is 1, subpool_is_free returns false.
+ The kernel skips freeing the subpool structure, leaking the
hugepage_subpool structure in kernel memory.
+ After patch:
+ Since used_hpages is 0, subpool_is_free returns true, and the subpool
structure is correctly freed.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
subpool_leak_max_size.sh | 72 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/subpool_leak_max_size.sh b/subpool_leak_max_size.sh
new file mode 100755
index 0000000000000..226fd2766c4d0
--- /dev/null
+++ b/subpool_leak_max_size.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+if [ "$EUID" -ne 0 ]; then
+ echo "Please run as root"
+ exit 1
+fi
+
+MNT_PATH="/tmp/mnt_hugetlb"
+FILE_PATH="$MNT_PATH/test_file"
+
+# Save original values
+orig_nr=$(cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages)
+orig_overcommit=$(cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_overcommit_hugepages)
+
+cleanup() {
+ echo "Cleaning up..."
+ rm -f "$FILE_PATH"
+ umount "$MNT_PATH" 2>/dev/null
+ rmdir "$MNT_PATH" 2>/dev/null
+ echo "$orig_nr" > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
+ echo "$orig_overcommit" > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_overcommit_hugepages
+ echo "Cleanup done."
+}
+trap cleanup EXIT
+
+# 1. Mount hugetlbfs with size=2M (1 page)
+mkdir -p "$MNT_PATH"
+if ! mount -t hugetlbfs -o size=2M none "$MNT_PATH"; then
+ echo "Failed to mount hugetlbfs"
+ exit 1
+fi
+
+# 2. Set nr_hugepages to 0, overcommit to 0
+echo 0 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
+echo 0 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_overcommit_hugepages
+
+# Check subpool usage before running
+read total free < <(stat -f -c "%b %f" "$MNT_PATH")
+used_before=$((total - free))
+echo "Before test - Subpool total blocks: $total"
+echo "Before test - Subpool free blocks: $free"
+echo "Before test - Subpool used blocks: $used_before"
+if [ "$used_before" -ne 0 ]; then
+ echo "ERROR: Subpool is not clean before test starts!"
+ exit 1
+fi
+
+# Run fallocate (expecting failure)
+echo "Running fallocate (expecting failure)..."
+if fallocate -l 2M "$FILE_PATH" 2>/dev/null; then
+ echo "ERROR: fallocate succeeded but should have failed (nr_hugepages is 0)"
+ exit 1
+fi
+
+# Check subpool usage via statfs
+# %b: Total blocks
+# %f: Free blocks
+read total free < <(stat -f -c "%b %f" "$MNT_PATH")
+used=$((total - free))
+
+echo "Subpool total blocks: $total"
+echo "Subpool free blocks: $free"
+echo "Subpool used blocks (leaked if > 0): $used"
+
+if [ "$used" -gt 0 ]; then
+ echo "RESULT: LEAK DETECTED (FAIL)"
+ exit 1
+else
+ echo "RESULT: NO LEAK (PASS)"
+ exit 0
+fi
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related
* [PATCH v4 13/16] WIP: Reproducer for allocation failure due to cgroup v2 memory limits
From: Ackerley Tng via B4 Relay @ 2026-07-22 23:41 UTC (permalink / raw)
To: Muchun Song, Oscar Salvador, David Hildenbrand, Joshua Hahn,
Shakeel Butt, Nhat Pham, Andrew Morton, Peter Xu, Wupeng Ma, fvdl,
rientjes, jthoughton, Mike Kravetz, Johannes Weiner, Michal Hocko,
Roman Gushchin, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Hongxiang Lou,
Miaohe Lin
Cc: vannapurve, erdemaktas, linux-mm, linux-kernel, cgroups,
linux-doc, Ackerley Tng
In-Reply-To: <20260722-hugetlb-alloc-failure-fixes-v4-0-88e8b81970dc@google.com>
From: Ackerley Tng <ackerleytng@google.com>
(This reproducer was hacked up and not meant to be merged.)
cgroup_v2_allocation_failure.c triggers HugeTLB allocation failure by exploiting
cgroup v2 memory limits. This allows testing the error paths in the kernel when
memory control charging fails, even when physical huge pages are available.
The program performs the following steps to trigger the failure:
1. Enable hugetlb accounting in cgroup v2.
+ The program checks if memory_hugetlb_accounting is enabled in the cgroup2
mount options. If not, it remounts /sys/fs/cgroup with this option
enabled. This ensures that HugeTLB allocations are charged against the
cgroup memory limits.
2. Create a test cgroup and set limits.
+ The program creates a new cgroup subdirectory named test_reproducer under
/sys/fs/cgroup.
+ It sets the memory.max limit of this cgroup to 1MB (which is less than
the 2MB huge page size).
3. Fork a child process and move it to the test cgroup.
+ The program forks a child process.
+ The child process moves itself into the test_reproducer cgroup by writing
its PID (using 0 for current process) to cgroup.procs in the test cgroup
directory.
4. Attempt to allocate and touch a 2MB huge page.
+ The child process maps a 2MB anonymous huge page using mmap with
MAP_PRIVATE, MAP_ANONYMOUS, and MAP_HUGETLB.
+ The child process writes to the mapped address, triggering a page fault.
5. Triggering the kernel bugs.
+ The page fault handler calls alloc_hugetlb_folio to allocate the huge
page.
+ The allocation of the physical page from buddy allocator succeeds
(assuming nr_hugepages is sufficient).
+ The kernel then attempts to charge this allocation to the child process's
cgroup by calling mem_cgroup_charge_hugetlb.
+ Since the child's cgroup memory limit is 1MB and the page is 2MB, the
charge fails and mem_cgroup_charge_hugetlb returns -ENOMEM.
+ This triggers the error path in alloc_hugetlb_folio where the bugs (folio
refcount mismatch, infinite loop on ENOMEM, and reservation leaks) are
handled.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
cgroup_v2_allocation_failure.c | 169 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
diff --git a/cgroup_v2_allocation_failure.c b/cgroup_v2_allocation_failure.c
new file mode 100644
index 0000000000000..1a813678d8cd2
--- /dev/null
+++ b/cgroup_v2_allocation_failure.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <errno.h>
+
+#define CGROUP_PATH "/sys/fs/cgroup"
+#define TEST_CGROUP "test_reproducer"
+#define TEST_CGROUP_PATH CGROUP_PATH "/" TEST_CGROUP
+
+static void write_file_val(const char *path, const char *val)
+{
+ int fd = open(path, O_WRONLY);
+
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
+ exit(1);
+ }
+ if (write(fd, val, strlen(val)) < 0) {
+ fprintf(stderr, "Failed to write %s to %s: %s\n", val, path, strerror(errno));
+ close(fd);
+ exit(1);
+ }
+ close(fd);
+}
+
+static int is_hugetlb_accounting_enabled(void)
+{
+ char spec[256], file[256], type[256], opts[512];
+ char line[1024];
+ int enabled = 0;
+ FILE *fp;
+
+ fp = fopen("/proc/mounts", "r");
+ if (!fp) {
+ perror("fopen /proc/mounts");
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line), fp)) {
+ if (sscanf(line, "%255s %255s %255s %511s", spec, file, type, opts) == 4) {
+ if (strcmp(file, CGROUP_PATH) == 0 && strcmp(type, "cgroup2") == 0) {
+ if (strstr(opts, "memory_hugetlb_accounting") != NULL)
+ enabled = 1;
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ return enabled;
+}
+
+static int enable_hugetlb_accounting(void)
+{
+ int ret;
+
+ printf("Attempting to remount cgroup2 with memory_hugetlb_accounting...\n");
+ ret = system("mount -o remount,memory_hugetlb_accounting " CGROUP_PATH);
+ if (ret != 0) {
+ fprintf(stderr, "Failed to remount: system() returned %d\n", ret);
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct stat st;
+ size_t size;
+ void *addr;
+ pid_t pid;
+ int enabled;
+ int status;
+ int fd;
+
+ if (stat(CGROUP_PATH, &st) != 0 || !S_ISDIR(st.st_mode)) {
+ fprintf(stderr, "cgroup v2 not mounted at %s\n", CGROUP_PATH);
+ return 1;
+ }
+
+ enabled = is_hugetlb_accounting_enabled();
+ if (enabled < 0)
+ return 1;
+
+ if (!enabled) {
+ if (enable_hugetlb_accounting() != 0) {
+ fprintf(stderr, "Could not enable memory_hugetlb_accounting\n");
+ return 1;
+ }
+ /* Re-check */
+ enabled = is_hugetlb_accounting_enabled();
+ if (enabled <= 0) {
+ fprintf(stderr, "Failed to enable memory_hugetlb_accounting (re-check failed)\n");
+ return 1;
+ }
+ printf("Successfully enabled memory_hugetlb_accounting\n");
+ } else {
+ printf("memory_hugetlb_accounting is already enabled\n");
+ }
+
+ /* Enable memory controller in subtree */
+ fd = open(CGROUP_PATH "/cgroup.subtree_control", O_WRONLY);
+ if (fd >= 0) {
+ (void)write(fd, "+memory", 7);
+ close(fd);
+ }
+
+ if (mkdir(TEST_CGROUP_PATH, 0755) != 0) {
+ if (errno != EEXIST) {
+ perror("mkdir test_reproducer");
+ return 1;
+ }
+ }
+
+ /* Set memory limit to 1MB (less than 2MB hugepage) */
+ write_file_val(TEST_CGROUP_PATH "/memory.max", "1M");
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* Child: Move to cgroup */
+ write_file_val(TEST_CGROUP_PATH "/cgroup.procs", "0");
+
+ printf("Child: Attempting to allocate and touch 2MB hugepage...\n");
+ /* Allocate 2MB hugepage */
+ size = 2 * 1024 * 1024;
+ addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+ if (addr == MAP_FAILED) {
+ perror("Child: mmap MAP_HUGETLB");
+ exit(1);
+ }
+
+ printf("Child: mmap succeeded at %p, touching it now...\n", addr);
+ *(char *)addr = 1;
+
+ printf("Child: Successfully touched page (bug not triggered?).\n");
+ munmap(addr, size);
+ exit(0);
+ }
+
+ /* Parent */
+ waitpid(pid, &status, 0);
+
+ printf("Parent: Child exited. Cleaning up.\n");
+ rmdir(TEST_CGROUP_PATH);
+
+ if (WIFSIGNALED(status)) {
+ printf("Parent: Child killed by signal %d (%s)\n",
+ WTERMSIG(status), strsignal(WTERMSIG(status)));
+ if (WTERMSIG(status) == SIGBUS)
+ printf("Parent: Child got SIGBUS as expected.\n");
+ } else if (WIFEXITED(status)) {
+ printf("Parent: Child exited with status %d\n", WEXITSTATUS(status));
+ }
+
+ return 0;
+}
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related
* [PATCH v4 12/16] WIP: tools: testing: Add unit tests for HugeTLB subpool functions
From: Ackerley Tng via B4 Relay @ 2026-07-22 23:41 UTC (permalink / raw)
To: Muchun Song, Oscar Salvador, David Hildenbrand, Joshua Hahn,
Shakeel Butt, Nhat Pham, Andrew Morton, Peter Xu, Wupeng Ma, fvdl,
rientjes, jthoughton, Mike Kravetz, Johannes Weiner, Michal Hocko,
Roman Gushchin, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Hongxiang Lou,
Miaohe Lin
Cc: vannapurve, erdemaktas, linux-mm, linux-kernel, cgroups,
linux-doc, Ackerley Tng
In-Reply-To: <20260722-hugetlb-alloc-failure-fixes-v4-0-88e8b81970dc@google.com>
From: Ackerley Tng <ackerleytng@google.com>
Introduce unit tests for HugeTLB subpool functions to exercise subpool
functions. Set testing up so that tests can be run directly from userspace.
Reuses the private kernel struct hugepage_subpool struct layout natively by
embedding the implementation directly, avoiding structural definition drift
between implementation and testing.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/hugetlb_subpool/.gitignore | 1 +
tools/testing/hugetlb_subpool/Makefile | 18 ++
tools/testing/hugetlb_subpool/test_subpool.c | 400 +++++++++++++++++++++++++++
3 files changed, 419 insertions(+)
diff --git a/tools/testing/hugetlb_subpool/.gitignore b/tools/testing/hugetlb_subpool/.gitignore
new file mode 100644
index 0000000000000..7348c2c72f1e1
--- /dev/null
+++ b/tools/testing/hugetlb_subpool/.gitignore
@@ -0,0 +1 @@
+test_subpool
diff --git a/tools/testing/hugetlb_subpool/Makefile b/tools/testing/hugetlb_subpool/Makefile
new file mode 100644
index 0000000000000..1bdb7e2635614
--- /dev/null
+++ b/tools/testing/hugetlb_subpool/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+.PHONY: all clean test
+
+CC = gcc
+CFLAGS = -Wall -O2 -I../shared -I. -I../../include -I../../arch/x86/include -pthread
+KERNEL_SUBPOOL_H = ../../../mm/hugetlb_subpool.h
+KERNEL_SUBPOOL_C = ../../../mm/hugetlb_subpool.c
+
+all: test
+
+test_subpool: test_subpool.c $(KERNEL_SUBPOOL_C) $(KERNEL_SUBPOOL_H)
+ $(CC) $(CFLAGS) test_subpool.c -o test_subpool
+
+test: test_subpool
+ ./test_subpool
+
+clean:
+ rm -f test_subpool
diff --git a/tools/testing/hugetlb_subpool/test_subpool.c b/tools/testing/hugetlb_subpool/test_subpool.c
new file mode 100644
index 0000000000000..78900274cc264
--- /dev/null
+++ b/tools/testing/hugetlb_subpool/test_subpool.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <assert.h>
+#include <stdlib.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/bug.h>
+
+/* Mocked Userspace implementation for Kernel Subpool allocation dependencies */
+struct hstate {
+ int dummy;
+};
+
+#undef kzalloc_obj
+#undef kzalloc_objs
+#define kzalloc_obj(P, ...) malloc(sizeof(P))
+#define kzalloc_objs(P, COUNT, ...) malloc(sizeof(P) * (COUNT))
+
+#define kfree free
+#define kmalloc malloc
+
+#define huge_page_shift(h) (21 + (0 * ((unsigned long)(h) & 0)))
+#define huge_page_size(h) (1UL << huge_page_shift(h))
+
+static bool hugetlb_acct_memory_called;
+static struct hstate *hugetlb_acct_memory_h;
+static long hugetlb_acct_memory_delta;
+
+static int hugetlb_acct_memory(struct hstate *h, long delta)
+{
+ hugetlb_acct_memory_called = true;
+ hugetlb_acct_memory_h = h;
+ hugetlb_acct_memory_delta = delta;
+ return 0;
+}
+
+static void reset_hugetlb_acct_memory_mock(void)
+{
+ hugetlb_acct_memory_called = false;
+ hugetlb_acct_memory_h = NULL;
+ hugetlb_acct_memory_delta = 0;
+}
+
+static void assert_hugetlb_acct_memory_called(struct hstate *h, long delta)
+{
+ assert(hugetlb_acct_memory_called);
+ assert(hugetlb_acct_memory_h == h);
+ assert(hugetlb_acct_memory_delta == delta);
+
+ reset_hugetlb_acct_memory_mock();
+}
+
+static void assert_hugetlb_acct_memory_not_called(void)
+{
+ assert(!hugetlb_acct_memory_called);
+}
+
+#include "../../../mm/hugetlb_subpool.h"
+#include "../../../mm/hugetlb_subpool.c"
+
+static void test_subpool_new_put_no_min_limit(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+
+ spool = hugepage_new_subpool(&h, 10, -1);
+ assert(spool != NULL);
+ assert(spool->max_hpages == 10);
+ assert(spool->min_hpages == -1);
+ assert(spool->rsv_hpages == -1);
+ assert(spool->count == 1);
+ assert_hugetlb_acct_memory_not_called();
+
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_not_called();
+}
+
+static void test_subpool_new_put_with_min_limit(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+
+ spool = hugepage_new_subpool(&h, 20, 5);
+ assert(spool != NULL);
+ assert(spool->max_hpages == 20);
+ assert(spool->min_hpages == 5);
+ assert(spool->rsv_hpages == 5);
+ assert(spool->count == 1);
+ assert_hugetlb_acct_memory_called(&h, 5);
+
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -5);
+}
+
+static void test_subpool_get_pages_below_min(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+ long ret;
+
+ /* Let's initialize: min_hpages = 10, used_hpages = 9, rsv_hpages = 1 */
+ spool = hugepage_new_subpool(&h, -1, 10);
+ assert_hugetlb_acct_memory_called(&h, 10);
+
+ ret = hugepage_subpool_get_pages(spool, 9);
+ assert(ret == 0);
+ assert(spool->used_hpages == 9);
+ assert(spool->rsv_hpages == 1);
+
+ /* Invoke Get (Consumes the remaining 1 subpool reserve!) */
+ ret = hugepage_subpool_get_pages(spool, 1);
+ assert(ret == 0); /* Covered by subpool reserve! */
+ assert(spool->used_hpages == 10);
+ assert(spool->rsv_hpages == 0);
+
+ /* Invoke Put (Replenishes the subpool reserve!) */
+ ret = hugepage_subpool_put_pages(spool, 1);
+ assert(ret == 0); /* Kept by subpool reserve! */
+ assert(spool->used_hpages == 9);
+ assert(spool->rsv_hpages == 1);
+
+ /* Cleanup: Return used_hpages to 0 so the subpool frees symmetrically! */
+ hugepage_subpool_put_pages(spool, 9);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -10);
+}
+
+static void test_subpool_get_pages_crossing_min(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+ long ret;
+
+ /* Let's initialize: min_hpages = 10, used_hpages = 10, rsv_hpages = 0 */
+ spool = hugepage_new_subpool(&h, -1, 10);
+ assert_hugetlb_acct_memory_called(&h, 10);
+
+ hugepage_subpool_get_pages(spool, 10);
+ assert(spool->used_hpages == 10);
+ assert(spool->rsv_hpages == 0);
+
+ /* Invoke Get (Triggers a request for a Global Buddy/Surplus page!) */
+ ret = hugepage_subpool_get_pages(spool, 1);
+ assert(ret == 1); /* Requires global page! */
+ assert(spool->used_hpages == 11);
+ assert(spool->rsv_hpages == 0);
+
+ /* Invoke Put (Above minimum, so it releases the page to the Global Pool!) */
+ ret = hugepage_subpool_put_pages(spool, 1);
+ assert(ret == 1); /* Dropped to global pool! */
+ assert(spool->used_hpages == 10);
+ assert(spool->rsv_hpages == 0);
+
+ /* Cleanup */
+ hugepage_subpool_put_pages(spool, 10);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -10);
+}
+
+static void test_subpool_get_pages_crossing_min_multi(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+ long ret;
+
+ /* Scenario 1: Crossing entirely into surplus territory by a delta > 1 */
+ /* Let's initialize: min_hpages = 10, used_hpages = 8, rsv_hpages = 2 */
+ spool = hugepage_new_subpool(&h, -1, 10);
+ assert_hugetlb_acct_memory_called(&h, 10);
+
+ ret = hugepage_subpool_get_pages(spool, 8);
+ assert(ret == 0);
+ assert(spool->used_hpages == 8);
+ assert(spool->rsv_hpages == 2);
+
+ /* Invoke Get with delta = 5 (Crosses min limit of 10 up to 13) */
+ ret = hugepage_subpool_get_pages(spool, 5);
+ assert(ret == 3); /* (8 + 5) - 10 = 3 global pages required! */
+ assert(spool->used_hpages == 13);
+ assert(spool->rsv_hpages == 0);
+
+ /* Invoke Put with delta = 5 (Drops from 13 down to 8) */
+ ret = hugepage_subpool_put_pages(spool, 5);
+ assert(ret == 3); /* 3 surplus pages released to the global pool! */
+ assert(spool->used_hpages == 8);
+ assert(spool->rsv_hpages == 2); /* 2 subpool reserves perfectly restored! */
+
+ /* Scenario 2: Landing exactly on the min_hpages boundary with delta > 1 */
+ ret = hugepage_subpool_get_pages(spool, 2);
+ assert(ret == 0); /* Perfectly covered by remaining 2 subpool reserves! */
+ assert(spool->used_hpages == 10);
+ assert(spool->rsv_hpages == 0);
+
+ ret = hugepage_subpool_put_pages(spool, 2);
+ assert(ret == 0); /* Swallowed perfectly to replenish the 2 subpool reserves! */
+ assert(spool->used_hpages == 8);
+ assert(spool->rsv_hpages == 2);
+
+ /* Cleanup */
+ hugepage_subpool_put_pages(spool, 8);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -10);
+}
+
+static void test_subpool_get_pages_max_limit(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+ long ret;
+
+ spool = hugepage_new_subpool(&h, 5, -1);
+ assert_hugetlb_acct_memory_not_called();
+
+ ret = hugepage_subpool_get_pages(spool, 5);
+ assert(ret == 5);
+ assert(spool->used_hpages == 5);
+ assert(spool->rsv_hpages == -1);
+
+ /* Invoke Get (Should trigger -ENOMEM due to max cap limit exceeded!) */
+ ret = hugepage_subpool_get_pages(spool, 1);
+ assert(ret == -ENOMEM);
+ assert(spool->used_hpages == 5); /* Unchanged */
+
+ /* Cleanup */
+ hugepage_subpool_put_pages(spool, 5);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_not_called();
+}
+
+static void test_subpool_get_pages_no_limits(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+ long ret;
+
+ spool = hugepage_new_subpool(&h, -1, -1);
+ assert_hugetlb_acct_memory_not_called();
+
+ hugepage_subpool_get_pages(spool, 5);
+ assert(spool->used_hpages == 5);
+
+ /* Invoke Get (Surplus Global Territory) */
+ ret = hugepage_subpool_get_pages(spool, 2);
+ assert(ret == 2);
+ assert(spool->used_hpages == 7);
+
+ /* Invoke Put */
+ ret = hugepage_subpool_put_pages(spool, 2);
+ assert(ret == 2);
+ assert(spool->used_hpages == 5);
+
+ /* Cleanup */
+ hugepage_subpool_put_pages(spool, 5);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_not_called();
+}
+
+static void test_subpool_free_hpages(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+
+ /* Test that free_hpages with NO min_size works perfectly */
+ spool = hugepage_new_subpool(&h, 15, -1);
+ hugepage_subpool_get_pages(spool, 3);
+ assert(hugepage_subpool_free_hpages(spool) == 12);
+ hugepage_subpool_put_pages(spool, 3);
+ hugepage_put_subpool(spool);
+
+ /* Test that free_hpages with a min_size configured is COMPLETELY UNAFFECTED by it */
+ spool = hugepage_new_subpool(&h, 15, 5);
+ assert_hugetlb_acct_memory_called(&h, 5);
+ hugepage_subpool_get_pages(spool, 3);
+ assert(hugepage_subpool_free_hpages(spool) == 12); /* Should still be 15 - 3 = 12! */
+ hugepage_subpool_put_pages(spool, 3);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -5);
+
+ spool = hugepage_new_subpool(&h, -1, -1);
+ hugepage_subpool_get_pages(spool, 3);
+ assert(hugepage_subpool_free_hpages(spool) == -1);
+ hugepage_subpool_put_pages(spool, 3);
+ hugepage_put_subpool(spool);
+
+ /* Test that free_hpages with a min_size configured and NO max size returns -1 */
+ spool = hugepage_new_subpool(&h, -1, 5);
+ assert_hugetlb_acct_memory_called(&h, 5);
+ hugepage_subpool_get_pages(spool, 3);
+ assert(hugepage_subpool_free_hpages(spool) == -1);
+ hugepage_subpool_put_pages(spool, 3);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -5);
+
+ spool = hugepage_new_subpool(&h, 3, -1);
+ hugepage_subpool_get_pages(spool, 3);
+ assert(hugepage_subpool_free_hpages(spool) == 0);
+ hugepage_subpool_put_pages(spool, 3);
+ hugepage_put_subpool(spool);
+}
+
+static void test_subpool_max_hpages(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+
+ spool = hugepage_new_subpool(&h, 123, -1);
+ assert(hugepage_subpool_max_hpages(spool) == 123);
+ hugepage_put_subpool(spool);
+
+ /* Test that max_hpages with a min_size configured is COMPLETELY UNAFFECTED by it */
+ spool = hugepage_new_subpool(&h, 123, 5);
+ assert_hugetlb_acct_memory_called(&h, 5);
+ assert(hugepage_subpool_max_hpages(spool) == 123);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -5);
+
+ spool = hugepage_new_subpool(&h, -1, -1);
+ assert(hugepage_subpool_max_hpages(spool) == -1);
+ hugepage_put_subpool(spool);
+
+ spool = hugepage_new_subpool(&h, -1, 5);
+ assert_hugetlb_acct_memory_called(&h, 5);
+ assert(hugepage_subpool_max_hpages(spool) == -1);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -5);
+
+ spool = hugepage_new_subpool(&h, 0, -1);
+ assert(hugepage_subpool_max_hpages(spool) == 0);
+ hugepage_put_subpool(spool);
+}
+
+static void test_subpool_max_size(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+
+ spool = hugepage_new_subpool(&h, 10, -1);
+ assert(hugepage_subpool_max_size(spool) == (10ULL << 21));
+ hugepage_put_subpool(spool);
+
+ /* Test that max_size with a min_size configured is COMPLETELY UNAFFECTED by it */
+ spool = hugepage_new_subpool(&h, 10, 5);
+ assert_hugetlb_acct_memory_called(&h, 5);
+ assert(hugepage_subpool_max_size(spool) == (10ULL << 21));
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -5);
+
+ spool = hugepage_new_subpool(&h, -1, -1);
+ assert(hugepage_subpool_max_size(spool) == -1ULL);
+ hugepage_put_subpool(spool);
+
+ spool = hugepage_new_subpool(&h, 0, -1);
+ assert(hugepage_subpool_max_size(spool) == 0ULL);
+ hugepage_put_subpool(spool);
+}
+
+static void test_subpool_min_size(void)
+{
+ struct hstate h;
+ struct hugepage_subpool *spool;
+
+ spool = hugepage_new_subpool(&h, -1, 5);
+ assert_hugetlb_acct_memory_called(&h, 5);
+ assert(hugepage_subpool_min_size(spool) == (5ULL << 21));
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -5);
+
+ /* Test that min_size with a max_size configured is COMPLETELY UNAFFECTED by it */
+ spool = hugepage_new_subpool(&h, 20, 5);
+ assert_hugetlb_acct_memory_called(&h, 5);
+ assert(hugepage_subpool_min_size(spool) == (5ULL << 21));
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, -5);
+
+ spool = hugepage_new_subpool(&h, -1, -1);
+ assert(hugepage_subpool_min_size(spool) == -1ULL);
+ hugepage_put_subpool(spool);
+
+ spool = hugepage_new_subpool(&h, -1, 0);
+ assert_hugetlb_acct_memory_called(&h, 0);
+ assert(hugepage_subpool_min_size(spool) == 0ULL);
+ hugepage_put_subpool(spool);
+ assert_hugetlb_acct_memory_called(&h, 0);
+}
+
+int main(void)
+{
+ test_subpool_new_put_no_min_limit();
+ test_subpool_new_put_with_min_limit();
+ test_subpool_get_pages_below_min();
+ test_subpool_get_pages_crossing_min();
+ test_subpool_get_pages_crossing_min_multi();
+ test_subpool_get_pages_max_limit();
+ test_subpool_get_pages_no_limits();
+ test_subpool_free_hpages();
+ test_subpool_max_hpages();
+ test_subpool_max_size();
+ test_subpool_min_size();
+
+ return 0;
+}
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related
* [PATCH v4 10/16] WIP: fs: hugetlbfs: Refactor subpool getters and integrate with hugetlb_subpool API
From: Ackerley Tng via B4 Relay @ 2026-07-22 23:41 UTC (permalink / raw)
To: Muchun Song, Oscar Salvador, David Hildenbrand, Joshua Hahn,
Shakeel Butt, Nhat Pham, Andrew Morton, Peter Xu, Wupeng Ma, fvdl,
rientjes, jthoughton, Mike Kravetz, Johannes Weiner, Michal Hocko,
Roman Gushchin, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Hongxiang Lou,
Miaohe Lin
Cc: vannapurve, erdemaktas, linux-mm, linux-kernel, cgroups,
linux-doc, Ackerley Tng
In-Reply-To: <20260722-hugetlb-alloc-failure-fixes-v4-0-88e8b81970dc@google.com>
From: Ackerley Tng <ackerleytng@google.com>
Refactor the direct field accesses to spool->max_hpages, spool->min_hpages,
and calculate subpool properties using getters inside
mm/hugetlb_subpool.c.
This will allow the definition of struct hugepage_subpool to be
encapsulated and private to mm/hugetlb_subpool.c
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
fs/hugetlbfs/inode.c | 28 +++++++++-------------------
mm/hugetlb_subpool.c | 39 ++++++++++++++++++++++++++++++++++++++-
mm/hugetlb_subpool.h | 4 ++++
3 files changed, 51 insertions(+), 20 deletions(-)
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 86c21f8272470..b424afdedb3ee 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -1060,7 +1060,6 @@ static int hugetlbfs_show_options(struct seq_file *m, struct dentry *root)
struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(root->d_sb);
struct hugepage_subpool *spool = sbinfo->spool;
unsigned long hpage_size = huge_page_size(sbinfo->hstate);
- unsigned hpage_shift = huge_page_shift(sbinfo->hstate);
char mod;
if (!uid_eq(sbinfo->uid, GLOBAL_ROOT_UID))
@@ -1082,12 +1081,13 @@ static int hugetlbfs_show_options(struct seq_file *m, struct dentry *root)
}
seq_printf(m, ",pagesize=%lu%c", hpage_size, mod);
if (spool) {
- if (spool->max_hpages != -1)
- seq_printf(m, ",size=%llu",
- (unsigned long long)spool->max_hpages << hpage_shift);
- if (spool->min_hpages != -1)
- seq_printf(m, ",min_size=%llu",
- (unsigned long long)spool->min_hpages << hpage_shift);
+ unsigned long long max_size = hugepage_subpool_max_size(spool);
+ unsigned long long min_size = hugepage_subpool_min_size(spool);
+
+ if (max_size != -1ULL)
+ seq_printf(m, ",size=%llu", max_size);
+ if (min_size != -1ULL)
+ seq_printf(m, ",min_size=%llu", min_size);
}
return 0;
}
@@ -1106,18 +1106,8 @@ static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf)
/* If no limits set, just report 0 or -1 for max/free/used
* blocks, like simple_statfs() */
if (sbinfo->spool) {
- long free_pages;
-
- spin_lock_irq(&sbinfo->spool->lock);
- buf->f_blocks = sbinfo->spool->max_hpages;
- if (sbinfo->spool->max_hpages == -1) {
- free_pages = -1;
- } else {
- free_pages = sbinfo->spool->max_hpages -
- sbinfo->spool->used_hpages;
- }
- buf->f_bavail = buf->f_bfree = free_pages;
- spin_unlock_irq(&sbinfo->spool->lock);
+ buf->f_blocks = hugepage_subpool_max_hpages(sbinfo->spool);
+ buf->f_bavail = buf->f_bfree = hugepage_subpool_free_hpages(sbinfo->spool);
buf->f_files = sbinfo->max_inodes;
buf->f_ffree = sbinfo->free_inodes;
}
diff --git a/mm/hugetlb_subpool.c b/mm/hugetlb_subpool.c
index 6184860ed7374..ac0f9057b4921 100644
--- a/mm/hugetlb_subpool.c
+++ b/mm/hugetlb_subpool.c
@@ -12,7 +12,6 @@
#endif
#include <linux/spinlock.h>
#include <linux/bug.h>
-
#include "hugetlb_subpool.h"
static inline bool subpool_is_free(struct hugepage_subpool *spool)
@@ -38,6 +37,44 @@ static inline void unlock_or_release_subpool(struct hugepage_subpool *spool,
}
}
+long hugepage_subpool_free_hpages(struct hugepage_subpool *spool)
+{
+ long free_pages;
+
+ spin_lock_irq(&spool->lock);
+ if (spool->max_hpages == -1)
+ free_pages = -1;
+ else
+ free_pages = spool->max_hpages - spool->used_hpages;
+ spin_unlock_irq(&spool->lock);
+
+ return free_pages;
+}
+
+static unsigned int hugepage_subpool_hpage_shift(struct hugepage_subpool *spool)
+{
+ return huge_page_shift(spool->hstate);
+}
+
+unsigned long long hugepage_subpool_max_size(struct hugepage_subpool *spool)
+{
+ if (spool->max_hpages == -1)
+ return -1ULL;
+ return (unsigned long long)spool->max_hpages << hugepage_subpool_hpage_shift(spool);
+}
+
+unsigned long long hugepage_subpool_min_size(struct hugepage_subpool *spool)
+{
+ if (spool->min_hpages == -1)
+ return -1ULL;
+ return (unsigned long long)spool->min_hpages << hugepage_subpool_hpage_shift(spool);
+}
+
+long hugepage_subpool_max_hpages(struct hugepage_subpool *spool)
+{
+ return spool->max_hpages;
+}
+
struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,
long min_hpages)
{
diff --git a/mm/hugetlb_subpool.h b/mm/hugetlb_subpool.h
index be1f1cf012c9c..41d22239f2c3e 100644
--- a/mm/hugetlb_subpool.h
+++ b/mm/hugetlb_subpool.h
@@ -13,5 +13,9 @@ struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,
void hugepage_put_subpool(struct hugepage_subpool *spool);
long hugepage_subpool_get_pages(struct hugepage_subpool *spool, long delta);
long hugepage_subpool_put_pages(struct hugepage_subpool *spool, long delta);
+long hugepage_subpool_free_hpages(struct hugepage_subpool *spool);
+long hugepage_subpool_max_hpages(struct hugepage_subpool *spool);
+unsigned long long hugepage_subpool_max_size(struct hugepage_subpool *spool);
+unsigned long long hugepage_subpool_min_size(struct hugepage_subpool *spool);
#endif /* _MM_HUGETLB_SUBPOOL_H */
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related
* [PATCH v4 09/16] WIP: mm: hugetlb: Move subpool functions to hugetlb_subpool.c
From: Ackerley Tng via B4 Relay @ 2026-07-22 23:41 UTC (permalink / raw)
To: Muchun Song, Oscar Salvador, David Hildenbrand, Joshua Hahn,
Shakeel Butt, Nhat Pham, Andrew Morton, Peter Xu, Wupeng Ma, fvdl,
rientjes, jthoughton, Mike Kravetz, Johannes Weiner, Michal Hocko,
Roman Gushchin, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Hongxiang Lou,
Miaohe Lin
Cc: vannapurve, erdemaktas, linux-mm, linux-kernel, cgroups,
linux-doc, Ackerley Tng
In-Reply-To: <20260722-hugetlb-alloc-failure-fixes-v4-0-88e8b81970dc@google.com>
From: Ackerley Tng <ackerleytng@google.com>
Move all HugeTLB subpool lifecycle, page reservation, and accounting
routines out of mm/hugetlb.c and into their own dedicated, encapsulated
translation unit at mm/hugetlb_subpool.c.
Also introduces the internal mm/hugetlb_subpool.h header for holding the
subpool-local APIs, allowing fs/hugetlbfs and mm/ to access the subpool
functions cleanly. The subpool internal layout structures remain in
include/linux/hugetlb.h until getters are introduced.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
fs/hugetlbfs/inode.c | 1 +
include/linux/hugetlb.h | 4 +-
mm/Makefile | 2 +-
mm/hugetlb.c | 166 +-------------------------------------------
mm/hugetlb_subpool.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++
mm/hugetlb_subpool.h | 17 +++++
6 files changed, 202 insertions(+), 169 deletions(-)
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index e5d86f31eba5b..86c21f8272470 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -25,6 +25,7 @@
#include <linux/ctype.h>
#include <linux/backing-dev.h>
#include <linux/hugetlb.h>
+#include "../../mm/hugetlb_subpool.h"
#include <linux/folio_batch.h>
#include <linux/fs_parser.h>
#include <linux/mman.h>
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 34b9a3e1be0fa..f36be371c6e88 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -114,9 +114,7 @@ extern int hugetlb_max_hstate __read_mostly;
#define for_each_hstate(h) \
for ((h) = hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++)
-struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,
- long min_hpages);
-void hugepage_put_subpool(struct hugepage_subpool *spool);
+int hugetlb_acct_memory(struct hstate *h, long delta);
void hugetlb_dup_vma_private(struct vm_area_struct *vma);
void clear_vma_resv_huge_pages(struct vm_area_struct *vma);
diff --git a/mm/Makefile b/mm/Makefile
index eff9f9e7e061c..3965c959e5099 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -78,7 +78,7 @@ endif
obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o
obj-$(CONFIG_ZSWAP) += zswap.o
obj-$(CONFIG_HAS_DMA) += dmapool.o
-obj-$(CONFIG_HUGETLBFS) += hugetlb.o hugetlb_sysfs.o hugetlb_sysctl.o
+obj-$(CONFIG_HUGETLBFS) += hugetlb.o hugetlb_subpool.o hugetlb_sysfs.o hugetlb_sysctl.o
ifdef CONFIG_CMA
obj-$(CONFIG_HUGETLBFS) += hugetlb_cma.o
endif
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 90ec015a11181..4d44a9720a971 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -51,6 +51,7 @@
#include "hugetlb_vmemmap.h"
#include "hugetlb_cma.h"
#include "hugetlb_internal.h"
+#include "hugetlb_subpool.h"
#include <linux/page-isolation.h>
int hugetlb_max_hstate __read_mostly;
@@ -126,171 +127,6 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
unsigned long start, unsigned long end, bool take_locks);
static struct resv_map *vma_resv_map(struct vm_area_struct *vma);
-static inline bool subpool_is_free(struct hugepage_subpool *spool)
-{
- if (spool->count)
- return false;
-
- return spool->used_hpages == 0;
-}
-
-static inline void unlock_or_release_subpool(struct hugepage_subpool *spool,
- unsigned long irq_flags)
-{
- bool is_free = subpool_is_free(spool);
-
- spin_unlock_irqrestore(&spool->lock, irq_flags);
-
- if (is_free) {
- if (spool->min_hpages != -1)
- hugetlb_acct_memory(spool->hstate,
- -spool->min_hpages);
- kfree(spool);
- }
-}
-
-struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,
- long min_hpages)
-{
- struct hugepage_subpool *spool;
-
- spool = kzalloc_obj(*spool);
- if (!spool)
- return NULL;
-
- spin_lock_init(&spool->lock);
- spool->count = 1;
- spool->max_hpages = max_hpages;
- spool->hstate = h;
- spool->min_hpages = min_hpages;
-
- if (min_hpages != -1 && hugetlb_acct_memory(h, min_hpages)) {
- kfree(spool);
- return NULL;
- }
- spool->rsv_hpages = min_hpages;
-
- return spool;
-}
-
-void hugepage_put_subpool(struct hugepage_subpool *spool)
-{
- unsigned long flags;
-
- if (!spool)
- return;
-
- spin_lock_irqsave(&spool->lock, flags);
- BUG_ON(!spool->count);
- spool->count--;
- unlock_or_release_subpool(spool, flags);
-}
-
-/**
- * hugepage_subpool_get_pages - Get pages from a subpool
- * @spool: pointer to subpool structure (may be NULL)
- * @delta: number of pages to allocate or reserve
- *
- * Check and update subpool page usage counts when allocating or
- * reserving @delta hugepages.
- *
- * Context: Takes spool->lock using spin_lock_irq().
- * Return: Non-negative number of reservations that cannot be
- * satisfied by the subpool, or -ENOMEM if the subpool maximum
- * limit would be exceeded.
- */
-static long hugepage_subpool_get_pages(struct hugepage_subpool *spool,
- long delta)
-{
- long ret = delta;
-
- if (!spool)
- return ret;
-
- spin_lock_irq(&spool->lock);
-
- if (spool->max_hpages != -1 &&
- spool->used_hpages + delta > spool->max_hpages) {
- ret = -ENOMEM;
- goto unlock_ret;
- }
-
- spool->used_hpages += delta;
-
- /* minimum size accounting */
- if (spool->min_hpages != -1 && spool->rsv_hpages) {
- if (delta > spool->rsv_hpages) {
- /*
- * Asking for more reserves than those already taken on
- * behalf of subpool. Return difference.
- */
- ret = delta - spool->rsv_hpages;
- spool->rsv_hpages = 0;
- } else {
- ret = 0; /* reserves already accounted for */
- spool->rsv_hpages -= delta;
- }
- }
-
-unlock_ret:
- spin_unlock_irq(&spool->lock);
- return ret;
-}
-
-/**
- * hugepage_subpool_put_pages - Release pages back to a subpool
- * @spool: pointer to subpool structure (may be NULL)
- * @delta: number of pages to free or unreserve
- *
- * Check and update subpool page usage counts when freeing or
- * unreserving @delta hugepages.
- *
- * Context: Takes spool->lock using spin_lock_irqsave(). May release
- * and free @spool if its usage count and references reach
- * zero.
- * Return: Non-negative number of reservations that the subpool cannot
- * absorb.
- */
-static long hugepage_subpool_put_pages(struct hugepage_subpool *spool,
- long delta)
-{
- long ret = delta;
- unsigned long flags;
-
- if (!spool)
- return delta;
-
- spin_lock_irqsave(&spool->lock, flags);
-
- spool->used_hpages -= delta;
-
- /* minimum size accounting */
- if (spool->min_hpages != -1 && spool->used_hpages < spool->min_hpages) {
- /*
- * limit is the maximum number of reservations that
- * can be restored to this subpool.
- */
- long limit = spool->min_hpages - spool->used_hpages;
-
- if (spool->rsv_hpages + delta <= limit)
- ret = 0;
- else
- ret = spool->rsv_hpages + delta - limit;
-
- spool->rsv_hpages += delta;
- if (spool->rsv_hpages > limit)
- spool->rsv_hpages = limit;
- }
-
- /*
- * If hugetlbfs_put_super couldn't free spool due to an outstanding
- * quota reference, free it now.
- */
- unlock_or_release_subpool(spool, flags);
-
- return ret;
-}
-
static inline struct hugepage_subpool *subpool_vma(struct vm_area_struct *vma)
{
return subpool_inode(file_inode(vma->vm_file));
diff --git a/mm/hugetlb_subpool.c b/mm/hugetlb_subpool.c
new file mode 100644
index 0000000000000..6184860ed7374
--- /dev/null
+++ b/mm/hugetlb_subpool.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Subpool and reserve accounting for HugeTLB folios.
+ * Extracted from mm/hugetlb.c
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#ifdef __KERNEL__
+#include <linux/hugetlb.h>
+#endif
+#include <linux/spinlock.h>
+#include <linux/bug.h>
+
+#include "hugetlb_subpool.h"
+
+static inline bool subpool_is_free(struct hugepage_subpool *spool)
+{
+ if (spool->count)
+ return false;
+
+ return spool->used_hpages == 0;
+}
+
+static inline void unlock_or_release_subpool(struct hugepage_subpool *spool,
+ unsigned long irq_flags)
+{
+ bool is_free = subpool_is_free(spool);
+
+ spin_unlock_irqrestore(&spool->lock, irq_flags);
+
+ if (is_free) {
+ if (spool->min_hpages != -1)
+ hugetlb_acct_memory(spool->hstate,
+ -spool->min_hpages);
+ kfree(spool);
+ }
+}
+
+struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,
+ long min_hpages)
+{
+ struct hugepage_subpool *spool;
+
+ spool = kzalloc_obj(*spool);
+ if (!spool)
+ return NULL;
+
+ spin_lock_init(&spool->lock);
+ spool->count = 1;
+ spool->max_hpages = max_hpages;
+ spool->hstate = h;
+ spool->min_hpages = min_hpages;
+
+ if (min_hpages != -1 && hugetlb_acct_memory(h, min_hpages)) {
+ kfree(spool);
+ return NULL;
+ }
+ spool->rsv_hpages = min_hpages;
+
+ return spool;
+}
+
+void hugepage_put_subpool(struct hugepage_subpool *spool)
+{
+ unsigned long flags;
+
+ if (!spool)
+ return;
+
+ spin_lock_irqsave(&spool->lock, flags);
+ BUG_ON(!spool->count);
+ spool->count--;
+ unlock_or_release_subpool(spool, flags);
+}
+
+/**
+ * hugepage_subpool_get_pages - Get pages from a subpool
+ * @spool: pointer to subpool structure (may be NULL)
+ * @delta: number of pages to allocate or reserve
+ *
+ * Check and update subpool page usage counts when allocating or
+ * reserving @delta hugepages.
+ *
+ * Context: Takes spool->lock using spin_lock_irq().
+ * Return: Non-negative number of reservations that cannot be
+ * satisfied by the subpool, or -ENOMEM if the subpool maximum
+ * limit would be exceeded.
+ */
+long hugepage_subpool_get_pages(struct hugepage_subpool *spool,
+ long delta)
+{
+ long ret = delta;
+
+ if (!spool)
+ return ret;
+
+ spin_lock_irq(&spool->lock);
+
+ if (spool->max_hpages != -1 &&
+ spool->used_hpages + delta > spool->max_hpages) {
+ ret = -ENOMEM;
+ goto unlock_ret;
+ }
+
+ spool->used_hpages += delta;
+
+ /* minimum size accounting */
+ if (spool->min_hpages != -1 && spool->rsv_hpages) {
+ if (delta > spool->rsv_hpages) {
+ /*
+ * Asking for more reserves than those already taken on
+ * behalf of subpool. Return difference.
+ */
+ ret = delta - spool->rsv_hpages;
+ spool->rsv_hpages = 0;
+ } else {
+ ret = 0; /* reserves already accounted for */
+ spool->rsv_hpages -= delta;
+ }
+ }
+
+unlock_ret:
+ spin_unlock_irq(&spool->lock);
+ return ret;
+}
+
+/**
+ * hugepage_subpool_put_pages - Release pages back to a subpool
+ * @spool: pointer to subpool structure (may be NULL)
+ * @delta: number of pages to free or unreserve
+ *
+ * Check and update subpool page usage counts when freeing or
+ * unreserving @delta hugepages.
+ *
+ * Context: Takes spool->lock using spin_lock_irqsave(). May release
+ * and free @spool if its usage count and references reach
+ * zero.
+ * Return: Non-negative number of reservations that the subpool cannot
+ * absorb.
+ */
+long hugepage_subpool_put_pages(struct hugepage_subpool *spool,
+ long delta)
+{
+ long ret = delta;
+ unsigned long flags;
+
+ if (!spool)
+ return delta;
+
+ spin_lock_irqsave(&spool->lock, flags);
+
+ spool->used_hpages -= delta;
+
+ /* minimum size accounting */
+ if (spool->min_hpages != -1 && spool->used_hpages < spool->min_hpages) {
+ /*
+ * limit is the maximum number of reservations that
+ * can be restored to this subpool.
+ */
+ long limit = spool->min_hpages - spool->used_hpages;
+
+ if (spool->rsv_hpages + delta <= limit)
+ ret = 0;
+ else
+ ret = spool->rsv_hpages + delta - limit;
+
+ spool->rsv_hpages += delta;
+ if (spool->rsv_hpages > limit)
+ spool->rsv_hpages = limit;
+ }
+
+ /*
+ * If hugetlbfs_put_super couldn't free spool due to an outstanding
+ * quota reference, free it now.
+ */
+ unlock_or_release_subpool(spool, flags);
+
+ return ret;
+}
diff --git a/mm/hugetlb_subpool.h b/mm/hugetlb_subpool.h
new file mode 100644
index 0000000000000..be1f1cf012c9c
--- /dev/null
+++ b/mm/hugetlb_subpool.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _MM_HUGETLB_SUBPOOL_H
+#define _MM_HUGETLB_SUBPOOL_H
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+struct hstate;
+struct hugepage_subpool;
+
+struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,
+ long min_hpages);
+void hugepage_put_subpool(struct hugepage_subpool *spool);
+long hugepage_subpool_get_pages(struct hugepage_subpool *spool, long delta);
+long hugepage_subpool_put_pages(struct hugepage_subpool *spool, long delta);
+
+#endif /* _MM_HUGETLB_SUBPOOL_H */
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related
* [PATCH v4 08/16] fs: hugetlbfs: Fix global reservation leak in hugetlbfs_fill_super()
From: Ackerley Tng via B4 Relay @ 2026-07-22 23:41 UTC (permalink / raw)
To: Muchun Song, Oscar Salvador, David Hildenbrand, Joshua Hahn,
Shakeel Butt, Nhat Pham, Andrew Morton, Peter Xu, Wupeng Ma, fvdl,
rientjes, jthoughton, Mike Kravetz, Johannes Weiner, Michal Hocko,
Roman Gushchin, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Hongxiang Lou,
Miaohe Lin
Cc: vannapurve, erdemaktas, linux-mm, linux-kernel, cgroups,
linux-doc, Ackerley Tng, stable
In-Reply-To: <20260722-hugetlb-alloc-failure-fixes-v4-0-88e8b81970dc@google.com>
From: Ackerley Tng <ackerleytng@google.com>
In hugetlbfs_fill_super(), if hugepage_new_subpool() succeeds in
allocating a subpool (which reserves global huge pages when
min_hpages != -1), but a subsequent initialization step like
d_make_root() fails, the error path directly invoked
kfree(sbinfo->spool).
Directly freeing the subpool structure with kfree() bypasses
hugepage_put_subpool() and its underlying unlock_or_release_subpool()
destructor. Consequently, hugetlb_acct_memory() is never called to
release the global page reservations allocated for min_hpages,
permanently leaking global huge page reservations.
Fix this leak by calling hugepage_put_subpool(sbinfo->spool) on the
error cleanup path instead of kfree(sbinfo->spool).
sbinfo->spool must be checked before dereferencing the subpool in
hugepage_put_subpool(). Add a NULL guard to hugepage_put_subpool() instead
of checking it in the caller to align it with other subpool helpers like
hugepage_subpool_get_pages() and hugepage_subpool_put_pages() that
gracefully handle NULL subpools. This allows callers to safely invoke
hugepage_put_subpool() without requiring explicit NULL checks. This is also
aligned with how kfree() can be called on NULL.
With the NULL guard in hugepage_put_subpool(), the NULL check in the only
other caller can also be removed.
Fixes: 7ca02d0ae586f ("hugetlbfs: accept subpool min_size mount option and setup accordingly")
Cc: stable@vger.kernel.org
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
fs/hugetlbfs/inode.c | 6 ++----
mm/hugetlb.c | 3 +++
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 26c0187340636..e5d86f31eba5b 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -1133,9 +1133,7 @@ static void hugetlbfs_put_super(struct super_block *sb)
if (sbi) {
sb->s_fs_info = NULL;
- if (sbi->spool)
- hugepage_put_subpool(sbi->spool);
-
+ hugepage_put_subpool(sbi->spool);
kfree(sbi);
}
}
@@ -1423,7 +1421,7 @@ hugetlbfs_fill_super(struct super_block *sb, struct fs_context *fc)
goto out_free;
return 0;
out_free:
- kfree(sbinfo->spool);
+ hugepage_put_subpool(sbinfo->spool);
kfree(sbinfo);
return -ENOMEM;
}
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index b759748468734..90ec015a11181 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -177,6 +177,9 @@ void hugepage_put_subpool(struct hugepage_subpool *spool)
{
unsigned long flags;
+ if (!spool)
+ return;
+
spin_lock_irqsave(&spool->lock, flags);
BUG_ON(!spool->count);
spool->count--;
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related
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